using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
    /// 
    /// Class MusicAlbumResolver
    /// 
    public class MusicAlbumResolver : ItemResolver
    {
        private readonly ILogger _logger;
        private readonly IFileSystem _fileSystem;
        public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem)
        {
            _logger = logger;
            _fileSystem = fileSystem;
        }
        /// 
        /// Gets the priority.
        /// 
        /// The priority.
        public override ResolverPriority Priority
        {
            get { return ResolverPriority.Third; } // we need to be ahead of the generic folder resolver but behind the movie one
        }
        /// 
        /// Resolves the specified args.
        /// 
        /// The args.
        /// MusicAlbum.
        protected override MusicAlbum Resolve(ItemResolveArgs args)
        {
            if (!args.IsDirectory) return null;
            //Avoid mis-identifying top folders
            if (args.Parent == null) return null;
            if (args.Parent.IsRoot) return null;
            if (args.Parent is MusicAlbum) return null;
            // Optimization
            if (args.Parent is BoxSet || args.Parent is Series || args.Parent is Season)
            {
                return null;
            }
            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;
            }
            return IsMusicAlbum(args, isMusicMediaFolder) ? new MusicAlbum() : null;
        }
        /// 
        /// Determine if the supplied file data points to a music album
        /// 
        /// The path.
        /// if set to true [is music media folder].
        /// The directory service.
        /// The logger.
        /// The file system.
        /// true if [is music album] [the specified data]; otherwise, false.
        public static bool IsMusicAlbum(string path, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
        {
            return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, true, directoryService, logger, fileSystem);
        }
        /// 
        /// Determine if the supplied resolve args should be considered a music album
        /// 
        /// The args.
        /// if set to true [is music media folder].
        /// true if [is music album] [the specified args]; otherwise, false.
        private bool IsMusicAlbum(ItemResolveArgs args, bool isMusicMediaFolder)
        {
            // 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, isMusicMediaFolder, true, args.DirectoryService, _logger, _fileSystem)) return true;
            }
            return false;
        }
        /// 
        /// Determine if the supplied list contains what we should consider music
        /// 
        /// The list.
        /// if set to true [is music media folder].
        /// if set to true [allow subfolders].
        /// The directory service.
        /// The logger.
        /// The file system.
        /// true if the specified list contains music; otherwise, false.
        private static bool ContainsMusic(IEnumerable list,
            bool isMusicMediaFolder,
            bool allowSubfolders,
            IDirectoryService directoryService,
            ILogger logger,
            IFileSystem fileSystem)
        {
            // If list contains at least 2 audio files or at least one and no video files consider it to contain music
            var foundAudio = 0;
            var discSubfolderCount = 0;
            foreach (var fileSystemInfo in list)
            {
                if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                {
                    if (isMusicMediaFolder && allowSubfolders && IsAlbumSubfolder(fileSystemInfo, true, directoryService, logger, fileSystem))
                    {
                        discSubfolderCount++;
                    }
                    if (!IsAdditionalSubfolderAllowed(fileSystemInfo))
                    {
                        return false;
                    }
                }
                var fullName = fileSystemInfo.FullName;
                if (EntityResolutionHelper.IsAudioFile(fullName))
                {
                    // Don't resolve these into audio files
                    if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename))
                    {
                        continue;
                    }
                    foundAudio++;
                }
                else if (EntityResolutionHelper.IsVideoFile(fullName)) return false;
                else if (EntityResolutionHelper.IsVideoPlaceHolder(fullName)) return false;
                if (foundAudio >= 2)
                {
                    return true;
                }
            }
            //  or a single audio file and no video files
            return foundAudio > 0 || discSubfolderCount > 0;
        }
        private static bool IsAlbumSubfolder(FileSystemInfo directory, bool isMusicMediaFolder, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem)
        {
            var path = directory.FullName;
            if (IsMultiDiscFolder(path))
            {
                logger.Debug("Found multi-disc folder: " + path);
                return ContainsMusic(directoryService.GetFileSystemEntries(path), isMusicMediaFolder, false, directoryService, logger, fileSystem);
            }
            return false;
        }
        public static bool IsMultiDiscFolder(string path)
        {
            return EntityResolutionHelper.IsMultiDiscAlbumFolder(path);
        }
        private static bool IsAdditionalSubfolderAllowed(FileSystemInfo directory)
        {
            // Resolver will ignore them based on rules engine
            return true;
        }
    }
}