// 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 osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;

namespace osu.Game.Skinning
{
    /// <summary>
    /// A drawable which has a callback when the skin changes.
    /// </summary>
    public abstract class SkinReloadableDrawable : CompositeDrawable
    {
        private readonly Func<ISkinSource, bool> allowFallback;
        private ISkinSource skin;

        /// <summary>
        /// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
        /// </summary>
        private bool allowDefaultFallback => allowFallback == null || allowFallback.Invoke(skin);

        /// <summary>
        /// Create a new <see cref="SkinReloadableDrawable"/>
        /// </summary>
        /// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
        protected SkinReloadableDrawable(Func<ISkinSource, bool> allowFallback = null)
        {
            this.allowFallback = allowFallback;
        }

        [BackgroundDependencyLoader]
        private void load(ISkinSource source)
        {
            skin = source;
            skin.SourceChanged += onChange;
        }

        private void onChange() =>
            // schedule required to avoid calls after disposed.
            // note that this has the side-effect of components only performing a skin change when they are alive.
            Scheduler.AddOnce(() => SkinChanged(skin, allowDefaultFallback));

        protected override void LoadAsyncComplete()
        {
            base.LoadAsyncComplete();
            SkinChanged(skin, allowDefaultFallback);
        }

        /// <summary>
        /// Called when a change is made to the skin.
        /// </summary>
        /// <param name="skin">The new skin.</param>
        /// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
        protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
        {
        }

        protected override void Dispose(bool isDisposing)
        {
            base.Dispose(isDisposing);

            if (skin != null)
                skin.SourceChanged -= onChange;
        }
    }
}