using PanCardView.Enums;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using static PanCardView.Processors.Constants;
using static System.Math;
using PanCardView.Utility;
using PanCardView.Extensions;
using System;
using System.IO;

namespace PanCardView.Processors
{
    public class CardsProcessor : IProcessor
    {
        public uint AnimationLength { get; set; } = 185;

        public Easing AnimationEasing { get; set; } = Easing.SinInOut;

        public double NoViewMaxShiftValue { get; set; } = 25;

        public double BackViewInitialScale { get; set; } = 0.8;

        public void Init(CardsView cardsView, params ProcessorItem[] items)
        {
            foreach (var item in items)
            {
                var view = item.Views.FirstOrDefault();
                ResetInitialState(view, cardsView, item.IsFront);
            }
        }

        public void Clean(CardsView cardsView, params ProcessorItem[] items)
        {
            foreach (var view in items.SelectMany(x => x.Views).Where(x => x != null) ?? Enumerable.Empty<View>())
            {
                view.IsVisible = false;
                view.TranslationX = cardsView.GetSize();
            }
        }

        public void Change(CardsView cardsView, double value, params ProcessorItem[] items)
        {
            foreach (var item in items)
            {
                var view = item.Views.FirstOrDefault();
                var inactiveView = item.InactiveViews.FirstOrDefault();

                if (view != null)
                {
                    view.IsVisible = true;
                }
                if (inactiveView != null && inactiveView != view)
                {
                    inactiveView.IsVisible = false;
                }

                if (item.IsFront)
                {

                    var multiplier = 1;
                    if (item.Direction == AnimationDirection.Null)
                    {
                        value = Sign(value) * Min(Abs(value / 4), NoViewMaxShiftValue);
                        multiplier = -multiplier;
                    }

                    if (view != null)
                    {
                        SetTranslationX(view, value, cardsView, true);
                        view.TranslationY = multiplier * Abs(value) / 10;
                        view.Rotation = multiplier * 0.3 * Rad * (value / cardsView.GetSize());
                    }
                    continue;
                }

                if (item.Direction == AnimationDirection.Null)
                {
                    continue;
                }

                var calcScale = BackViewInitialScale + Abs((value / cardsView.RealMoveDistance) * (1 - BackViewInitialScale));
                view.Scale = Min(calcScale, 1);
            }
        }

        public async Task Navigate(CardsView cardsView, params ProcessorItem[] items)
        {
            var animations = new List<Task>();
            ProcessorItem? endItem = null;
            foreach (var item in items)
            {
                var view = item.Views.FirstOrDefault();
                if (view == null)
                {
                    continue;
                }

                if (item.IsFront)
                {

                    view.IsVisible = true;
                    animations.Add(new AnimationWrapper(v => {
                        if (double.IsNaN(v))
                        {
                            return;
                        }
                        view.Scale = v;
                    }, view.Scale, 1).Commit(view, Path.GetRandomFileName(), 16, AnimationLength, AnimationEasing));
                    continue;
                }

                animations.Add(new AnimationWrapper(v => HandleAutoAnimatingPosChanged(view, cardsView, v, item.Direction), 0, cardsView.RealMoveDistance)
                    .Commit(view, Path.GetRandomFileName(), 16, AnimationLength, AnimationEasing));
                endItem = item;
            }
            await Task.WhenAll(animations);
            if (endItem.HasValue)
            {
                await Proceed(cardsView, endItem.GetValueOrDefault());
            }
        }

        public async Task Proceed(CardsView cardsView, params ProcessorItem[] items)
        {
            var animation = new AnimationWrapper();
            Action onFinish = null;
            foreach (var item in items)
            {
                var view = item.Views.FirstOrDefault();

                if (item.IsFront)
                {
                    if (view != null)
                    {
                        view.Scale = 1;
                    }
                    continue;
                }

                if (view != null)
                {
                    animation.Add(0, 1, new AnimationWrapper(v => view.Opacity = v, view.Opacity, 0));
                    onFinish = () => view.IsVisible = false;
                }
            }
            await animation.Commit(cardsView, Path.GetRandomFileName(), 16, AnimationLength, AnimationEasing);
            onFinish?.Invoke();
        }

        public async Task Reset(CardsView cardsView, params ProcessorItem[] items)
        {
            var animation = new AnimationWrapper();
            var animLength = AnimationLength;
            Action onFinish = null;
            foreach (var item in items)
            {
                var view = item.Views.FirstOrDefault();

                if (item.IsFront)
                {
                    onFinish = () => ResetInitialState(view, cardsView, true);
                    animLength = (uint)(AnimationLength * Min(Abs(view.TranslationX / cardsView.RealMoveDistance), 1.0));

                    if (animLength == 0)
                    {
                        continue;
                    }

                    animation.Add(0, 1, new AnimationWrapper {
                        { 0, 1, new AnimationWrapper (v => SetTranslationX(view, v, cardsView, true), view.TranslationX, 0) },
                        { 0, 1, new AnimationWrapper (v => view.TranslationY = v, view.TranslationY, 0) },
                        { 0, 1, new AnimationWrapper (v => view.Rotation = v, view.Rotation, 0) }
                    });
                    continue;
                }
                if (view == null || view == cardsView.CurrentView)
                {
                    continue;
                }
                animation.Add(0, 1, new AnimationWrapper(v => {
                    if(double.IsNaN(v))
                    {
                        return;
                    }
                    view.Scale = v;
                }, view.Scale, BackViewInitialScale));
            }
            await animation.Commit(cardsView, Path.GetRandomFileName(), 16, animLength, AnimationEasing);
            onFinish?.Invoke();
        }

        protected virtual void ResetInitialState(View view, CardsView cardsView, bool isFront)
        {
            if (view != null)
            {
                view.BatchBegin();
                view.Scale = isFront ? 1 : BackViewInitialScale;
                view.Opacity = 1;
                SetTranslationX(view, 0, cardsView, isFront, isFront);
                view.Rotation = 0;
                view.TranslationY = 0;
                view.BatchCommit();
            }
        }

        protected virtual void SetTranslationX(View view, double value, CardsView cardsView, bool isFront, bool? isVisible = null)
        {
            view.TranslationX = value;
            view.IsVisible = isVisible ?? view.IsVisible;
            if (isFront)
            {
                cardsView.ProcessorDiff = value;
            }
        }

        private void HandleAutoAnimatingPosChanged(View view, CardsView cardsView, double value, AnimationDirection animationDirection)
        {
            if (animationDirection == AnimationDirection.Next)
            {
                value = -value;
            }

            view.TranslationX = value;
            view.TranslationY = Abs(value) / 10;
            view.Rotation = 0.3 * Min(value / cardsView.GetSize(), 1) * Rad;
        }
    }
}