
using System.Diagnostics.CodeAnalysis;

namespace Voile.Resources
{
    /// <summary>
    /// Loads resources from various sources and prepares them to be used for Voile.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class ResourceLoader<T> where T : Resource
    {
        /// <summary>
        /// File extensions that are supported by this loader.
        /// </summary>
        public abstract IEnumerable<string> SupportedExtensions { get; }

        /// <summary>
        /// Loads a resource to <see cref="ResourceManager"/>'s resource list.
        /// </summary>
        /// <param name="path">File system path to the resource to load.</param>
        /// <returns>A <see cref="Guid"/> of the loaded resource that can be later retrieved with <see cref="TryGet"/>.</returns>
        public Guid Load(string path)
        {
            var resource = LoadResource(path);
            var guid = Guid.NewGuid();

            var loadedResources = ResourceManager.LoadedResources;
            var oldResourceGuid = loadedResources.FirstOrDefault(loadedResource => loadedResource.Value.Path == path).Key;

            if (loadedResources.ContainsKey(oldResourceGuid))
            {
                ResourceManager.ReplaceResource(oldResourceGuid, resource);
                return oldResourceGuid;
            }

            ResourceManager.AddResource(guid, resource);

            return guid;
        }

        /// <summary>
        /// Reloads resources loaded by this resource loader.
        /// </summary>
        public void Reload()
        {
            foreach (var loadedResource in ResourceManager.LoadedResources)
            {
                if (loadedResource.Value is not T) continue;
                Load(loadedResource.Value.Path);
            }
        }

        /// <summary>
        /// Unloads a resource.
        /// </summary>
        /// <param name="resourceGuid">GUID of a resource to unload.</param>
        /// <returns>True if unloading was successful, otherwise false.</returns>
        public bool TryUnload(Guid resourceGuid)
        {
            if (!ResourceManager.LoadedResources.ContainsKey(resourceGuid))
            {
                return false;
            }

            var resource = ResourceManager.LoadedResources[resourceGuid];

            ResourceManager.RemoveResource(resourceGuid);

            if (resource is IDisposable disposable)
            {
                disposable.Dispose();
            }


            return true;
        }

        /// <summary>
        /// Load steps specific to this resource loader.
        /// </summary>
        /// <param name="path">File system path to the resource to load.</param>
        /// <returns>Loaded resource.</returns>
        protected abstract T LoadResource(string path);
    }
}