// 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.

using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Overlays.Direct
{
    public class PlayButton : Container
    {
        public readonly BindableBool Playing = new BindableBool();
        public PreviewTrack Preview { get; private set; }

        private BeatmapSetInfo beatmapSet;

        public BeatmapSetInfo BeatmapSet
        {
            get => beatmapSet;
            set
            {
                if (value == beatmapSet) return;

                beatmapSet = value;

                Preview?.Stop();
                Preview?.Expire();
                Preview = null;

                Playing.Value = false;
            }
        }

        private Color4 hoverColour;
        private readonly SpriteIcon icon;
        private readonly LoadingSpinner loadingSpinner;

        private const float transition_duration = 500;

        private bool loading
        {
            set
            {
                if (value)
                {
                    icon.FadeTo(0.5f, transition_duration, Easing.OutQuint);
                    loadingSpinner.Show();
                }
                else
                {
                    icon.FadeTo(1, transition_duration, Easing.OutQuint);
                    loadingSpinner.Hide();
                }
            }
        }

        public PlayButton(BeatmapSetInfo setInfo = null)
        {
            BeatmapSet = setInfo;
            AddRange(new Drawable[]
            {
                icon = new SpriteIcon
                {
                    Anchor = Anchor.Centre,
                    Origin = Anchor.Centre,
                    FillMode = FillMode.Fit,
                    RelativeSizeAxes = Axes.Both,
                    Icon = FontAwesome.Solid.Play,
                },
                loadingSpinner = new LoadingSpinner
                {
                    Size = new Vector2(15),
                },
            });

            Playing.ValueChanged += playingStateChanged;
        }

        [Resolved]
        private PreviewTrackManager previewTrackManager { get; set; }

        [BackgroundDependencyLoader]
        private void load(OsuColour colour)
        {
            hoverColour = colour.Yellow;
        }

        protected override bool OnClick(ClickEvent e)
        {
            Playing.Toggle();
            return true;
        }

        protected override bool OnHover(HoverEvent e)
        {
            icon.FadeColour(hoverColour, 120, Easing.InOutQuint);
            return base.OnHover(e);
        }

        protected override void OnHoverLost(HoverLostEvent e)
        {
            if (!Playing.Value)
                icon.FadeColour(Color4.White, 120, Easing.InOutQuint);
            base.OnHoverLost(e);
        }

        private void playingStateChanged(ValueChangedEvent<bool> e)
        {
            icon.Icon = e.NewValue ? FontAwesome.Solid.Stop : FontAwesome.Solid.Play;
            icon.FadeColour(e.NewValue || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);

            if (e.NewValue)
            {
                if (BeatmapSet == null)
                {
                    Playing.Value = false;
                    return;
                }

                if (Preview != null)
                {
                    attemptStart();
                    return;
                }

                loading = true;

                LoadComponentAsync(Preview = previewTrackManager.Get(beatmapSet), preview =>
                {
                    // beatmapset may have changed.
                    if (Preview != preview)
                        return;

                    AddInternal(preview);
                    loading = false;
                    preview.Stopped += () => Playing.Value = false;

                    // user may have changed their mind.
                    if (Playing.Value)
                        attemptStart();
                });
            }
            else
            {
                Preview?.Stop();
                loading = false;
            }
        }

        private void attemptStart()
        {
            if (Preview?.Start() != true)
                Playing.Value = false;
        }

        protected override void Dispose(bool isDisposing)
        {
            base.Dispose(isDisposing);
            Playing.Value = false;
        }
    }
}
