using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Audio;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
    /// 
    /// Class MusicAlbumResolver.
    /// 
    public class MusicAlbumResolver : ItemResolver
    {
        private readonly ILogger _logger;
        private readonly IFileSystem _fileSystem;
        private readonly ILibraryManager _libraryManager;
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The logger.
        /// The file system.
        /// The library manager.
        public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager)
        {
            _logger = logger;
            _fileSystem = fileSystem;
            _libraryManager = libraryManager;
        }
        /// 
        /// Gets the priority.
        /// 
        /// The priority.
        public override ResolverPriority Priority => ResolverPriority.Second;
        /// 
        /// Resolves the specified args.
        /// 
        /// The args.
        /// MusicAlbum.
        protected override MusicAlbum Resolve(ItemResolveArgs args)
        {
            var collectionType = args.GetCollectionType();
            var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
            // If there's a collection type and it's not music, don't allow it.
            if (!isMusicMediaFolder)
            {
                return null;
            }
            if (!args.IsDirectory)
            {
                return null;
            }
            // Avoid mis-identifying top folders
            if (args.HasParent())
            {
                return null;
            }
            if (args.Parent.IsRoot)
            {
                return null;
            }
            return IsMusicAlbum(args) ? new MusicAlbum() : null;
        }
        /// 
        /// Determine if the supplied file data points to a music album.
        /// 
        public bool IsMusicAlbum(string path, IDirectoryService directoryService)
        {
            return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
        }
        /// 
        /// Determine if the supplied resolve args should be considered a music album.
        /// 
        /// The args.
        /// true if [is music album] [the specified args]; otherwise, false.
        private bool IsMusicAlbum(ItemResolveArgs args)
        {
            // Args points to an album if parent is an Artist folder or it directly contains music
            if (args.IsDirectory)
            {
                // if (args.Parent is MusicArtist) return true;  // saves us from testing children twice
                if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
                {
                    return true;
                }
            }
            return false;
        }
        /// 
        /// Determine if the supplied list contains what we should consider music.
        /// 
        private bool ContainsMusic(
            IEnumerable list,
            bool allowSubfolders,
            IDirectoryService directoryService,
            ILogger logger,
            IFileSystem fileSystem,
            ILibraryManager libraryManager)
        {
            // check for audio files before digging down into directories
            var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName));
            if (foundAudioFile)
            {
                // at least one audio file exists
                return true;
            }
            if (!allowSubfolders)
            {
                // not music since no audio file exists and we're not looking into subfolders
                return false;
            }
            var discSubfolderCount = 0;
            var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
            var parser = new AlbumParser(namingOptions);
            var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
            var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
            {
                var path = fileSystemInfo.FullName;
                var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
                if (hasMusic)
                {
                    if (parser.IsMultiPart(path))
                    {
                        logger.LogDebug("Found multi-disc folder: " + path);
                        Interlocked.Increment(ref discSubfolderCount);
                    }
                    else
                    {
                        // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
                        state.Stop();
                    }
                }
            });
            if (!result.IsCompleted)
            {
                return false;
            }
            return discSubfolderCount > 0;
        }
    }
}