// 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 System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.Editors;
using osu.Game.Tournament.Screens.Ladder.Components;
using osuTK.Graphics;

namespace osu.Game.Tournament.Screens.Ladder
{
    public partial class LadderScreen : TournamentScreen
    {
        protected Container<DrawableTournamentMatch> MatchesContainer = null!;
        private Container<Path> paths = null!;
        private Container headings = null!;

        protected LadderDragContainer ScrollContent = null!;

        protected Container Content = null!;

        [BackgroundDependencyLoader]
        private void load()
        {
            normalPathColour = Color4Extensions.FromHex("#66D1FF");
            losersPathColour = Color4Extensions.FromHex("#FFC700");

            RelativeSizeAxes = Axes.Both;

            InternalChild = Content = new Container
            {
                RelativeSizeAxes = Axes.Both,
                Masking = true,
                Children = new Drawable[]
                {
                    new TourneyVideo("ladder")
                    {
                        RelativeSizeAxes = Axes.Both,
                        Loop = true,
                    },
                    new DrawableTournamentHeaderText
                    {
                        Y = 100,
                        Anchor = Anchor.TopCentre,
                        Origin = Anchor.TopCentre,
                    },
                    ScrollContent = new LadderDragContainer
                    {
                        AutoSizeAxes = Axes.Both,
                        Children = new Drawable[]
                        {
                            paths = new Container<Path> { RelativeSizeAxes = Axes.Both },
                            headings = new Container { RelativeSizeAxes = Axes.Both },
                            MatchesContainer = new Container<DrawableTournamentMatch>
                            {
                                AutoSizeAxes = Axes.Both
                            },
                        }
                    },
                }
            };

            void addMatch(TournamentMatch match) =>
                MatchesContainer.Add(new DrawableTournamentMatch(match, this is LadderEditorScreen)
                {
                    Changed = () => layout.Invalidate()
                });

            foreach (var match in LadderInfo.Matches)
                addMatch(match);

            LadderInfo.Rounds.CollectionChanged += (_, _) => layout.Invalidate();
            LadderInfo.Matches.CollectionChanged += (_, args) =>
            {
                switch (args.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        Debug.Assert(args.NewItems != null);

                        foreach (var p in args.NewItems.Cast<TournamentMatch>())
                            addMatch(p);
                        break;

                    case NotifyCollectionChangedAction.Remove:
                        Debug.Assert(args.OldItems != null);

                        foreach (var p in args.OldItems.Cast<TournamentMatch>())
                        {
                            foreach (var d in MatchesContainer.Where(d => d.Match == p))
                                d.Expire();
                        }

                        break;
                }

                layout.Invalidate();
            };
        }

        private readonly Cached layout = new Cached();

        protected override void Update()
        {
            base.Update();

            if (!layout.IsValid)
                UpdateLayout();
        }

        private Color4 normalPathColour;
        private Color4 losersPathColour;

        protected virtual bool DrawLoserPaths => false;

        protected virtual void UpdateLayout()
        {
            paths.Clear();
            headings.Clear();

            int id = 1;

            foreach (var match in MatchesContainer.OrderBy(d => d.Y).ThenBy(d => d.X))
            {
                match.Match.ID = id++;

                if (match.Match.Progression.Value != null)
                {
                    var dest = MatchesContainer.FirstOrDefault(p => p.Match == match.Match.Progression.Value);

                    if (dest == null)
                        // clean up outdated progressions.
                        match.Match.Progression.Value = null;
                    else
                        paths.Add(new ProgressionPath(match, dest) { Colour = match.Match.Losers.Value ? losersPathColour : normalPathColour });
                }

                if (DrawLoserPaths)
                {
                    if (match.Match.LosersProgression.Value != null)
                    {
                        var dest = MatchesContainer.FirstOrDefault(p => p.Match == match.Match.LosersProgression.Value);

                        if (dest == null)
                            // clean up outdated progressions.
                            match.Match.LosersProgression.Value = null;
                        else
                            paths.Add(new ProgressionPath(match, dest) { Colour = losersPathColour.Opacity(0.1f) });
                    }
                }
            }

            foreach (var round in LadderInfo.Rounds)
            {
                var topMatch = MatchesContainer.Where(p => !p.Match.Losers.Value && p.Match.Round.Value == round).MinBy(p => p.Y);

                if (topMatch == null) continue;

                headings.Add(new DrawableTournamentRound(round)
                {
                    Position = headings.ToLocalSpace((topMatch.ScreenSpaceDrawQuad.TopLeft + topMatch.ScreenSpaceDrawQuad.TopRight) / 2),
                    Margin = new MarginPadding { Bottom = 10 },
                    Origin = Anchor.BottomCentre,
                });
            }

            foreach (var round in LadderInfo.Rounds)
            {
                var topMatch = MatchesContainer.Where(p => p.Match.Losers.Value && p.Match.Round.Value == round).MinBy(p => p.Y);

                if (topMatch == null) continue;

                headings.Add(new DrawableTournamentRound(round, true)
                {
                    Position = headings.ToLocalSpace((topMatch.ScreenSpaceDrawQuad.TopLeft + topMatch.ScreenSpaceDrawQuad.TopRight) / 2),
                    Margin = new MarginPadding { Bottom = 10 },
                    Origin = Anchor.BottomCentre,
                });
            }

            layout.Validate();
        }
    }
}
