// 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;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Online;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Tests.Visual.Components
{
    [HeadlessTest]
    public class TestScenePollingComponent : OsuTestScene
    {
        private Container pollBox;
        private TestPoller poller;

        private const float safety_adjust = 1f;
        private int count;

        [SetUp]
        public void SetUp() => Schedule(() =>
        {
            count = 0;

            Children = new Drawable[]
            {
                pollBox = new Container
                {
                    Alpha = 0,
                    RelativeSizeAxes = Axes.Both,
                    Children = new Drawable[]
                    {
                        new Box
                        {
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre,
                            Scale = new Vector2(0.4f),
                            Colour = Color4.LimeGreen,
                            RelativeSizeAxes = Axes.Both,
                        },
                        new OsuSpriteText
                        {
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre,
                            Text = "Poll!",
                        }
                    }
                }
            };
        });

        [Test]
        [Ignore("polling is threaded, and it's very hard to hook into it correctly")]
        public void TestInstantPolling()
        {
            createPoller(true);

            AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust);
            checkCount(1);
            checkCount(2);
            checkCount(3);

            AddStep("set poll interval to 5", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5);
            checkCount(4);
            checkCount(4);
            checkCount(4);

            skip();

            checkCount(5);
            checkCount(5);

            AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust);
            checkCount(6);
            checkCount(7);
        }

        [Test]
        [Ignore("i have no idea how to fix the timing of this one")]
        public void TestSlowPolling()
        {
            createPoller(false);

            AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5);
            checkCount(0);
            skip();
            checkCount(0);
            skip();
            skip();
            checkCount(0);
            skip();
            skip();
            checkCount(0);
        }

        private void skip() => AddStep("skip", () =>
        {
            // could be 4 or 5 at this point due to timing discrepancies (safety_adjust @ 0.2 * 5 ~= 1)
            // easiest to just ignore the value at this point and move on.
        });

        private void checkCount(int checkValue)
        {
            AddAssert($"count is {checkValue}", () =>
            {
                Logger.Log($"value is {count}");
                return count == checkValue;
            });
        }

        private void createPoller(bool instant) => AddStep("create poller", () =>
        {
            poller?.Expire();

            Add(poller = instant ? new TestPoller() : new TestSlowPoller());
            poller.OnPoll += () =>
            {
                pollBox.FadeOutFromOne(500);
                count++;
            };
        });

        protected override double TimePerAction => 500;

        public class TestPoller : PollingComponent
        {
            public event Action OnPoll;

            protected override Task Poll()
            {
                Schedule(() => OnPoll?.Invoke());
                return base.Poll();
            }
        }

        public class TestSlowPoller : TestPoller
        {
            protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls.Value / 2f / Clock.Rate)).ContinueWith(_ => base.Poll());
        }
    }
}