// 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;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;

namespace osu.Game.Beatmaps
{
    /// <summary>
    /// A <see cref="Bindable{WorkingBeatmap}"/> for the <see cref="OsuGame"/> beatmap.
    /// This should be used sparingly in-favour of <see cref="IBindableBeatmap"/>.
    /// </summary>
    public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>, IBindableBeatmap
    {
        private AudioManager audioManager;
        private WorkingBeatmap lastBeatmap;

        protected BindableBeatmap(WorkingBeatmap defaultValue)
            : base(defaultValue)
        {
        }

        /// <summary>
        /// Registers an <see cref="AudioManager"/> for <see cref="Track"/>s to be added to.
        /// </summary>
        /// <param name="audioManager">The <see cref="AudioManager"/> to register.</param>
        protected void RegisterAudioManager([NotNull] AudioManager audioManager)
        {
            if (this.audioManager != null) throw new InvalidOperationException($"Cannot register multiple {nameof(AudioManager)}s.");

            this.audioManager = audioManager;

            ValueChanged += registerAudioTrack;

            // If the track has changed prior to this being called, let's register it
            if (Value != Default)
                registerAudioTrack(Value);
        }

        private void registerAudioTrack(WorkingBeatmap beatmap)
        {
            var trackLoaded = lastBeatmap?.TrackLoaded ?? false;

            // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
            if (!trackLoaded || lastBeatmap?.Track != beatmap.Track)
            {
                if (trackLoaded)
                {
                    Debug.Assert(lastBeatmap != null);
                    Debug.Assert(lastBeatmap.Track != null);

                    lastBeatmap.RecycleTrack();
                }

                audioManager.Track.AddItem(beatmap.Track);
            }

            lastBeatmap = beatmap;
        }

        [NotNull]
        IBindableBeatmap IBindableBeatmap.GetBoundCopy() => GetBoundCopy();

        /// <summary>
        /// Retrieve a new <see cref="BindableBeatmap"/> instance weakly bound to this <see cref="BindableBeatmap"/>.
        /// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
        /// </summary>
        [NotNull]
        public abstract BindableBeatmap GetBoundCopy();
    }
}