// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;

namespace osu.Game.Screens.Menu
{
    public partial class IntroSequence : Container
    {
        private const float logo_size = 460; //todo: this should probably be 480

        private OsuSpriteText welcomeText;

        private Container<Box> lines;

        private Box lineTopLeft;
        private Box lineBottomLeft;
        private Box lineTopRight;
        private Box lineBottomRight;

        private Ring smallRing;
        private Ring mediumRing;
        private Ring bigRing;

        private Box backgroundFill;
        private Box foregroundFill;

        private CircularContainer pinkCircle;
        private CircularContainer blueCircle;
        private CircularContainer yellowCircle;
        private CircularContainer purpleCircle;

        public IntroSequence()
        {
            RelativeSizeAxes = Axes.Both;
            Alpha = 0;
        }

        [BackgroundDependencyLoader]
        private void load()
        {
            const int line_offset = 80;
            const int circle_offset = 250;

            Children = new Drawable[]
            {
                lines = new Container<Box>
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    AutoSizeAxes = Axes.Both,
                    Children = new[]
                    {
                        lineTopLeft = new Box
                        {
                            Origin = Anchor.CentreLeft,
                            Anchor = Anchor.Centre,
                            Position = new Vector2(-line_offset, -line_offset),
                            Rotation = 45,
                            Colour = Color4.White.Opacity(180),
                        },
                        lineTopRight = new Box
                        {
                            Origin = Anchor.CentreRight,
                            Anchor = Anchor.Centre,
                            Position = new Vector2(line_offset, -line_offset),
                            Rotation = -45,
                            Colour = Color4.White.Opacity(80),
                        },
                        lineBottomLeft = new Box
                        {
                            Origin = Anchor.CentreLeft,
                            Anchor = Anchor.Centre,
                            Position = new Vector2(-line_offset, line_offset),
                            Rotation = -45,
                            Colour = Color4.White.Opacity(230),
                        },
                        lineBottomRight = new Box
                        {
                            Origin = Anchor.CentreRight,
                            Anchor = Anchor.Centre,
                            Position = new Vector2(line_offset, line_offset),
                            Rotation = 45,
                            Colour = Color4.White.Opacity(130),
                        },
                    }
                },
                bigRing = new Ring(Color4Extensions.FromHex(@"B6C5E9"), 0.85f),
                mediumRing = new Ring(Color4.White.Opacity(130), 0.7f),
                smallRing = new Ring(Color4.White, 0.6f),
                welcomeText = new OsuSpriteText
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    Text = "welcome",
                    Padding = new MarginPadding { Bottom = 10 },
                    Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
                    Alpha = 0,
                    Spacing = new Vector2(5),
                },
                new CircularContainer
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    Size = new Vector2(logo_size),
                    Masking = true,
                    Children = new Drawable[]
                    {
                        backgroundFill = new Box
                        {
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre,
                            RelativeSizeAxes = Axes.Both,
                            Height = 0,
                            Colour = Color4Extensions.FromHex(@"C6D8FF").Opacity(160),
                        },
                        foregroundFill = new Box
                        {
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre,
                            Size = Vector2.Zero,
                            RelativeSizeAxes = Axes.Both,
                            Width = 0,
                            Colour = Color4.White,
                        },
                    }
                },
                purpleCircle = new Circle
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.TopCentre,
                    Position = new Vector2(0, circle_offset),
                    Colour = Color4Extensions.FromHex(@"AA92FF"),
                },
                blueCircle = new Circle
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.CentreRight,
                    Position = new Vector2(-circle_offset, 0),
                    Colour = Color4Extensions.FromHex(@"8FE5FE"),
                },
                yellowCircle = new Circle
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.BottomCentre,
                    Position = new Vector2(0, -circle_offset),
                    Colour = Color4Extensions.FromHex(@"FFD64C"),
                },
                pinkCircle = new Circle
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.CentreLeft,
                    Position = new Vector2(circle_offset, 0),
                    Colour = Color4Extensions.FromHex(@"e967a1"),
                },
            };

            foreach (var line in lines)
            {
                line.Size = new Vector2(105, 1.5f);
                line.Alpha = 0;
            }

            Scale = new Vector2(0.5f);
        }

        public void Start(double length)
        {
            if (Children.Any())
            {
                // restart if we were already run previously.
                FinishTransforms(true);
                load();
            }

            smallRing.ResizeTo(logo_size * 0.086f, 400, Easing.InOutQuint);

            mediumRing.ResizeTo(130, 340, Easing.OutQuad);
            mediumRing.Foreground.ResizeTo(1, 880, Easing.Out);

            double remainingTime() => length - TransformDelay;

            using (BeginDelayedSequence(250))
            {
                welcomeText.FadeIn(700);
                welcomeText.TransformSpacingTo(new Vector2(20, 0), remainingTime(), Easing.Out);

                const int line_duration = 700;
                const int line_resize = 150;

                foreach (var line in lines)
                {
                    line.FadeIn(40).ResizeWidthTo(0, line_duration - line_resize, Easing.OutQuint);
                }

                const int line_end_offset = 120;

                smallRing.Foreground.ResizeTo(1, line_duration, Easing.OutQuint);
                smallRing.Delay(400).FadeColour(Color4.Black, 300);

                lineTopLeft.MoveTo(new Vector2(-line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
                lineTopRight.MoveTo(new Vector2(line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
                lineBottomLeft.MoveTo(new Vector2(-line_end_offset, line_end_offset), line_duration, Easing.OutQuint);
                lineBottomRight.MoveTo(new Vector2(line_end_offset, line_end_offset), line_duration, Easing.OutQuint);

                using (BeginDelayedSequence(length * 0.56))
                {
                    bigRing.ResizeTo(logo_size, 500, Easing.InOutQuint);
                    bigRing.Foreground.Delay(250).ResizeTo(1, 850, Easing.OutQuint);

                    using (BeginDelayedSequence(250))
                    {
                        backgroundFill.ResizeHeightTo(1, remainingTime(), Easing.InOutQuart);
                        backgroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);

                        using (BeginDelayedSequence(50))
                        {
                            foregroundFill.ResizeWidthTo(1, remainingTime(), Easing.InOutQuart);
                            foregroundFill.RotateTo(-90, remainingTime(), Easing.InOutQuart);
                        }

                        this.ScaleTo(1, remainingTime(), Easing.InOutCubic);

                        const float circle_size = logo_size * 0.9f;

                        const int rotation_delay = 110;
                        const int appear_delay = 80;

                        purpleCircle.MoveToY(circle_size / 2, remainingTime(), Easing.InOutQuart);
                        purpleCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
                        purpleCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);

                        using (BeginDelayedSequence(appear_delay))
                        {
                            yellowCircle.MoveToY(-circle_size / 2, remainingTime(), Easing.InOutQuart);
                            yellowCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
                            yellowCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);

                            using (BeginDelayedSequence(appear_delay))
                            {
                                blueCircle.MoveToX(-circle_size / 2, remainingTime(), Easing.InOutQuart);
                                blueCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
                                blueCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);

                                using (BeginDelayedSequence(appear_delay))
                                {
                                    pinkCircle.MoveToX(circle_size / 2, remainingTime(), Easing.InOutQuart);
                                    pinkCircle.Delay(rotation_delay).RotateTo(-180, remainingTime() - rotation_delay, Easing.InOutQuart);
                                    pinkCircle.ResizeTo(circle_size, remainingTime(), Easing.InOutQuart);
                                }
                            }
                        }
                    }
                }
            }
        }

        private partial class Ring : Container<Circle>
        {
            public readonly Circle Foreground;

            public Ring(Color4 ringColour, float foregroundSize)
            {
                Anchor = Anchor.Centre;
                Origin = Anchor.Centre;
                Children = new[]
                {
                    new Circle
                    {
                        Anchor = Anchor.Centre,
                        Origin = Anchor.Centre,
                        RelativeSizeAxes = Axes.Both,
                        Scale = new Vector2(0.98f),
                        Colour = ringColour,
                    },
                    Foreground = new Circle
                    {
                        Size = new Vector2(foregroundSize),
                        Anchor = Anchor.Centre,
                        Origin = Anchor.Centre,
                        RelativeSizeAxes = Axes.Both,
                        Colour = Color4.Black,
                    }
                };
            }
        }
    }
}