using MediaBrowser.Common.IO;
using MediaBrowser.Common.Kernel;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playback;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.ScheduledTasks;
using MediaBrowser.Controller.Updates;
using MediaBrowser.Controller.Weather;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller
{
    /// 
    /// Class Kernel
    /// 
    public class Kernel : BaseKernel
    {
        /// 
        /// The MB admin URL
        /// 
        public const string MBAdminUrl = "http://mb3admin.com/admin/";
        /// 
        /// Gets the instance.
        /// 
        /// The instance.
        public static Kernel Instance { get; private set; }
        /// 
        /// Gets the library manager.
        /// 
        /// The library manager.
        public LibraryManager LibraryManager { get; private set; }
        /// 
        /// Gets the image manager.
        /// 
        /// The image manager.
        public ImageManager ImageManager { get; private set; }
        /// 
        /// Gets the user manager.
        /// 
        /// The user manager.
        public UserManager UserManager { get; private set; }
        /// 
        /// Gets the FFMPEG controller.
        /// 
        /// The FFMPEG controller.
        public FFMpegManager FFMpegManager { get; private set; }
        /// 
        /// Gets the installation manager.
        /// 
        /// The installation manager.
        public InstallationManager InstallationManager { get; private set; }
        /// 
        /// Gets or sets the file system manager.
        /// 
        /// The file system manager.
        public FileSystemManager FileSystemManager { get; private set; }
        /// 
        /// Gets the provider manager.
        /// 
        /// The provider manager.
        public ProviderManager ProviderManager { get; private set; }
        /// 
        /// Gets the user data manager.
        /// 
        /// The user data manager.
        public UserDataManager UserDataManager { get; private set; }
        /// 
        /// Gets the plug-in security manager.
        /// 
        /// The plug-in security manager.
        public PluginSecurityManager PluginSecurityManager { get; private set; }
        /// 
        /// The _users
        /// 
        private IEnumerable _users;
        /// 
        /// The _user lock
        /// 
        private object _usersSyncLock = new object();
        /// 
        /// The _users initialized
        /// 
        private bool _usersInitialized;
        /// 
        /// Gets the users.
        /// 
        /// The users.
        public IEnumerable Users
        {
            get
            {
                // Call ToList to exhaust the stream because we'll be iterating over this multiple times
                LazyInitializer.EnsureInitialized(ref _users, ref _usersInitialized, ref _usersSyncLock, UserManager.LoadUsers);
                return _users;
            }
            internal set
            {
                _users = value;
                if (value == null)
                {
                    _usersInitialized = false;
                }
            }
        }
        /// 
        /// The _root folder
        /// 
        private AggregateFolder _rootFolder;
        /// 
        /// The _root folder sync lock
        /// 
        private object _rootFolderSyncLock = new object();
        /// 
        /// The _root folder initialized
        /// 
        private bool _rootFolderInitialized;
        /// 
        /// Gets the root folder.
        /// 
        /// The root folder.
        public AggregateFolder RootFolder
        {
            get
            {
                LazyInitializer.EnsureInitialized(ref _rootFolder, ref _rootFolderInitialized, ref _rootFolderSyncLock, LibraryManager.CreateRootFolder);
                return _rootFolder;
            }
            private set
            {
                _rootFolder = value;
                if (value == null)
                {
                    _rootFolderInitialized = false;
                }
            }
        }
        /// 
        /// Gets the kernel context.
        /// 
        /// The kernel context.
        public override KernelContext KernelContext
        {
            get { return KernelContext.Server; }
        }
        /// 
        /// Gets the list of plugin configuration pages
        /// 
        /// The configuration pages.
        [ImportMany(typeof(BaseConfigurationPage))]
        public IEnumerable PluginConfigurationPages { get; private set; }
        /// 
        /// Gets the intro providers.
        /// 
        /// The intro providers.
        [ImportMany(typeof(IIntroProvider))]
        public IEnumerable IntroProviders { get; private set; }
        /// 
        /// Gets the list of currently registered weather prvoiders
        /// 
        /// The weather providers.
        [ImportMany(typeof(IWeatherProvider))]
        public IEnumerable WeatherProviders { get; private set; }
        /// 
        /// Gets the list of currently registered metadata prvoiders
        /// 
        /// The metadata providers enumerable.
        [ImportMany(typeof(BaseMetadataProvider))]
        public BaseMetadataProvider[] MetadataProviders { get; private set; }
        /// 
        /// Gets the list of currently registered image processors
        /// Image processors are specialized metadata providers that run after the normal ones
        /// 
        /// The image enhancers.
        [ImportMany(typeof(BaseImageEnhancer))]
        public BaseImageEnhancer[] ImageEnhancers { get; private set; }
        /// 
        /// Gets the list of currently registered entity resolvers
        /// 
        /// The entity resolvers enumerable.
        [ImportMany(typeof(IBaseItemResolver))]
        internal IBaseItemResolver[] EntityResolvers { get; private set; }
        /// 
        /// Gets the list of BasePluginFolders added by plugins
        /// 
        /// The plugin folders.
        [ImportMany(typeof(BasePluginFolder))]
        internal IEnumerable PluginFolders { get; private set; }
        /// 
        /// Gets the list of available user repositories
        /// 
        /// The user repositories.
        [ImportMany(typeof(IUserRepository))]
        private IEnumerable UserRepositories { get; set; }
        /// 
        /// Gets the active user repository
        /// 
        /// The user repository.
        public IUserRepository UserRepository { get; private set; }
        /// 
        /// Gets the active user repository
        /// 
        /// The display preferences repository.
        public IDisplayPreferencesRepository DisplayPreferencesRepository { get; private set; }
        /// 
        /// Gets the list of available item repositories
        /// 
        /// The item repositories.
        [ImportMany(typeof(IItemRepository))]
        private IEnumerable ItemRepositories { get; set; }
        /// 
        /// Gets the active item repository
        /// 
        /// The item repository.
        public IItemRepository ItemRepository { get; private set; }
        /// 
        /// Gets the list of available item repositories
        /// 
        /// The user data repositories.
        [ImportMany(typeof(IUserDataRepository))]
        private IEnumerable UserDataRepositories { get; set; }
        /// 
        /// Gets the list of available DisplayPreferencesRepositories
        /// 
        /// The display preferences repositories.
        [ImportMany(typeof(IDisplayPreferencesRepository))]
        private IEnumerable DisplayPreferencesRepositories { get; set; }
        /// 
        /// Gets the list of entity resolution ignore rules
        /// 
        /// The entity resolution ignore rules.
        [ImportMany(typeof(BaseResolutionIgnoreRule))]
        internal IEnumerable EntityResolutionIgnoreRules { get; private set; }
        /// 
        /// Gets the active user data repository
        /// 
        /// The user data repository.
        public IUserDataRepository UserDataRepository { get; private set; }
        /// 
        /// Limits simultaneous access to various resources
        /// 
        /// The resource pools.
        public ResourcePool ResourcePools { get; set; }
        /// 
        /// Gets the UDP server port number.
        /// 
        /// The UDP server port number.
        public override int UdpServerPortNumber
        {
            get { return 7359; }
        }
        /// 
        /// Gets or sets the zip client.
        /// 
        /// The zip client.
        private IZipClient ZipClient { get; set; }
        /// 
        /// Gets or sets the bluray examiner.
        /// 
        /// The bluray examiner.
        private IBlurayExaminer BlurayExaminer { get; set; }
        
        /// 
        /// Creates a kernel based on a Data path, which is akin to our current programdata path
        /// 
        public Kernel(IIsoManager isoManager, IZipClient zipClient, IBlurayExaminer blurayExaminer)
            : base(isoManager)
        {
            if (isoManager == null)
            {
                throw new ArgumentNullException("isoManager");
            }
            if (zipClient == null)
            {
                throw new ArgumentNullException("zipClient");
            }
            if (blurayExaminer == null)
            {
                throw new ArgumentNullException("blurayExaminer");
            }
            
            Instance = this;
            ZipClient = zipClient;
            BlurayExaminer = blurayExaminer;
        }
        /// 
        /// Composes the exported values.
        /// 
        /// The container.
        protected override void ComposeExportedValues(CompositionContainer container)
        {
            base.ComposeExportedValues(container);
            container.ComposeExportedValue("kernel", this);
            container.ComposeExportedValue("blurayExaminer", BlurayExaminer);
        }
        /// 
        /// Performs initializations that can be reloaded at anytime
        /// 
        /// Task.
        protected override async Task ReloadInternal()
        {
            Logger.Info("Extracting tools");
            // Reset these so that they can be lazy loaded again
            Users = null;
            RootFolder = null;
            ReloadResourcePools();
            InstallationManager = new InstallationManager(this, ZipClient);
            LibraryManager = new LibraryManager(this);
            UserManager = new UserManager(this);
            FFMpegManager = new FFMpegManager(this, ZipClient);
            ImageManager = new ImageManager(this);
            ProviderManager = new ProviderManager(this);
            UserDataManager = new UserDataManager(this);
            PluginSecurityManager = new PluginSecurityManager(this);
            await base.ReloadInternal().ConfigureAwait(false);
            ReloadFileSystemManager();
            await UserManager.RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
        }
        /// 
        /// Releases unmanaged and - optionally - managed resources.
        /// 
        /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
        protected override void Dispose(bool dispose)
        {
            if (dispose)
            {
                DisposeResourcePools();
                DisposeFileSystemManager();
            }
            base.Dispose(dispose);
        }
        /// 
        /// Disposes the resource pools.
        /// 
        private void DisposeResourcePools()
        {
            if (ResourcePools != null)
            {
                ResourcePools.Dispose();
                ResourcePools = null;
            }
        }
        /// 
        /// Reloads the resource pools.
        /// 
        private void ReloadResourcePools()
        {
            DisposeResourcePools();
            ResourcePools = new ResourcePool();
        }
        /// 
        /// Called when [composable parts loaded].
        /// 
        /// Task.
        protected override async Task OnComposablePartsLoaded()
        {
            // The base class will start up all the plugins
            await base.OnComposablePartsLoaded().ConfigureAwait(false);
            // Get the current item repository
            ItemRepository = GetRepository(ItemRepositories, Configuration.ItemRepository);
            var itemRepoTask = ItemRepository.Initialize();
            // Get the current user repository
            UserRepository = GetRepository(UserRepositories, Configuration.UserRepository);
            var userRepoTask = UserRepository.Initialize();
            // Get the current item repository
            UserDataRepository = GetRepository(UserDataRepositories, Configuration.UserDataRepository);
            var userDataRepoTask = UserDataRepository.Initialize();
            // Get the current display preferences repository
            DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, Configuration.DisplayPreferencesRepository);
            var displayPreferencesRepoTask = DisplayPreferencesRepository.Initialize();
            // Sort the resolvers by priority
            EntityResolvers = EntityResolvers.OrderBy(e => e.Priority).ToArray();
            // Sort the providers by priority
            MetadataProviders = MetadataProviders.OrderBy(e => e.Priority).ToArray();
            // Sort the image processors by priority
            ImageEnhancers = ImageEnhancers.OrderBy(e => e.Priority).ToArray();
            await Task.WhenAll(itemRepoTask, userRepoTask, userDataRepoTask, displayPreferencesRepoTask).ConfigureAwait(false);
        }
        /// 
        /// Gets a repository by name from a list, and returns the default if not found
        /// 
        /// 
        /// The repositories.
        /// The name.
        /// ``0.
        private T GetRepository(IEnumerable repositories, string name)
            where T : class, IRepository
        {
            var enumerable = repositories as T[] ?? repositories.ToArray();
            return enumerable.FirstOrDefault(r => string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)) ??
                   enumerable.FirstOrDefault();
        }
        /// 
        /// Disposes the file system manager.
        /// 
        private void DisposeFileSystemManager()
        {
            if (FileSystemManager != null)
            {
                FileSystemManager.Dispose();
                FileSystemManager = null;
            }
        }
        /// 
        /// Reloads the file system manager.
        /// 
        private void ReloadFileSystemManager()
        {
            DisposeFileSystemManager();
            FileSystemManager = new FileSystemManager(this);
            FileSystemManager.StartWatchers();
        }
        /// 
        /// Gets a User by Id
        /// 
        /// The id.
        /// User.
        /// 
        public User GetUserById(Guid id)
        {
            if (id == Guid.Empty)
            {
                throw new ArgumentNullException();
            }
            return Users.FirstOrDefault(u => u.Id == id);
        }
        /// 
        /// Finds a library item by Id and UserId.
        /// 
        /// The id.
        /// The user id.
        /// BaseItem.
        /// id
        public BaseItem GetItemById(Guid id, Guid userId)
        {
            if (id == Guid.Empty)
            {
                throw new ArgumentNullException("id");
            }
            if (userId == Guid.Empty)
            {
                throw new ArgumentNullException("userId");
            }
            var user = GetUserById(userId);
            var userRoot = user.RootFolder;
            return userRoot.FindItemById(id, user);
        }
        /// 
        /// Gets the item by id.
        /// 
        /// The id.
        /// BaseItem.
        /// id
        public BaseItem GetItemById(Guid id)
        {
            if (id == Guid.Empty)
            {
                throw new ArgumentNullException("id");
            }
            return RootFolder.FindItemById(id, null);
        }
        /// 
        /// Completely overwrites the current configuration with a new copy
        /// 
        /// The config.
        public void UpdateConfiguration(ServerConfiguration config)
        {
            var oldConfiguration = Configuration;
            var reloadLogger = config.ShowLogWindow != oldConfiguration.ShowLogWindow;
            // Figure out whether or not we should refresh people after the update is finished
            var refreshPeopleAfterUpdate = !oldConfiguration.EnableInternetProviders && config.EnableInternetProviders;
            // This is true if internet providers has just been turned on, or if People have just been removed from InternetProviderExcludeTypes
            if (!refreshPeopleAfterUpdate)
            {
                var oldConfigurationFetchesPeopleImages = oldConfiguration.InternetProviderExcludeTypes == null || !oldConfiguration.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
                var newConfigurationFetchesPeopleImages = config.InternetProviderExcludeTypes == null || !config.InternetProviderExcludeTypes.Contains(typeof(Person).Name, StringComparer.OrdinalIgnoreCase);
                refreshPeopleAfterUpdate = newConfigurationFetchesPeopleImages && !oldConfigurationFetchesPeopleImages;
            }
            Configuration = config;
            SaveConfiguration();
            if (reloadLogger)
            {
                ReloadLogger();
            }
            TcpManager.OnApplicationConfigurationChanged(oldConfiguration, config);
            // Validate currently executing providers, in the background
            Task.Run(() =>
            {
                ProviderManager.ValidateCurrentlyRunningProviders();
                // Any number of configuration settings could change the way the library is refreshed, so do that now
                TaskManager.CancelIfRunningAndQueue();
                if (refreshPeopleAfterUpdate)
                {
                    TaskManager.CancelIfRunningAndQueue();
                }
            });
        }
        /// 
        /// Removes the plugin.
        /// 
        /// The plugin.
        internal void RemovePlugin(IPlugin plugin)
        {
            var list = Plugins.ToList();
            list.Remove(plugin);
            Plugins = list;
        }
        /// 
        /// Gets the system info.
        /// 
        /// SystemInfo.
        public override SystemInfo GetSystemInfo()
        {
            var info = base.GetSystemInfo();
            if (InstallationManager != null)
            {
                info.InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray();
                info.CompletedInstallations = InstallationManager.CompletedInstallations.ToArray();
            }
            return info;
        }
    }
}