using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
{
    /// 
    /// Class BaseItem
    /// 
    public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo
    {
        protected BaseItem()
        {
            Genres = new List();
            Studios = new List();
            People = new List();
            ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
            LockedFields = new List();
            ImageInfos = new List();
            Identities = new List();
        }
        /// 
        /// The supported image extensions
        /// 
        public static readonly string[] SupportedImageExtensions = new[] { ".png", ".jpg", ".jpeg", ".tbn" };
        /// 
        /// The trailer folder name
        /// 
        public const string TrailerFolderName = "trailers";
        public const string ThemeSongsFolderName = "theme-music";
        public const string ThemeSongFilename = "theme";
        public const string ThemeVideosFolderName = "backdrops";
        public const string XbmcTrailerFileSuffix = "-trailer";
        public List ImageInfos { get; set; }
        /// 
        /// Gets a value indicating whether this instance is in mixed folder.
        /// 
        /// true if this instance is in mixed folder; otherwise, false.
        public bool IsInMixedFolder { get; set; }
        private string _name;
        /// 
        /// Gets or sets the name.
        /// 
        /// The name.
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                // lazy load this again
                _sortName = null;
            }
        }
        /// 
        /// Gets or sets the id.
        /// 
        /// The id.
        public Guid Id { get; set; }
        /// 
        /// Return the id that should be used to key display prefs for this item.
        /// Default is based on the type for everything except actual generic folders.
        /// 
        /// The display prefs id.
        [IgnoreDataMember]
        public virtual Guid DisplayPreferencesId
        {
            get
            {
                var thisType = GetType();
                return thisType == typeof(Folder) ? Id : thisType.FullName.GetMD5();
            }
        }
        /// 
        /// Gets or sets the path.
        /// 
        /// The path.
        public virtual string Path { get; set; }
        [IgnoreDataMember]
        protected internal bool IsOffline { get; set; }
        /// 
        /// Returns the folder containing the item.
        /// If the item is a folder, it returns the folder itself
        /// 
        [IgnoreDataMember]
        public virtual string ContainingFolderPath
        {
            get
            {
                if (IsFolder)
                {
                    return Path;
                }
                return System.IO.Path.GetDirectoryName(Path);
            }
        }
        [IgnoreDataMember]
        public virtual bool IsHidden
        {
            get
            {
                return false;
            }
        }
        [IgnoreDataMember]
        public virtual bool IsOwnedItem
        {
            get
            {
                // Local trailer, special feature, theme video, etc.
                // An item that belongs to another item but is not part of the Parent-Child tree
                return !IsFolder && Parent == null;
            }
        }
        /// 
        /// Gets or sets the type of the location.
        /// 
        /// The type of the location.
        [IgnoreDataMember]
        public virtual LocationType LocationType
        {
            get
            {
                if (IsOffline)
                {
                    return LocationType.Offline;
                }
                if (string.IsNullOrEmpty(Path))
                {
                    return LocationType.Virtual;
                }
                return System.IO.Path.IsPathRooted(Path) ? LocationType.FileSystem : LocationType.Remote;
            }
        }
        public virtual bool SupportsLocalMetadata
        {
            get
            {
                var locationType = LocationType;
                return locationType != LocationType.Remote && locationType != LocationType.Virtual;
            }
        }
        /// 
        /// This is just a helper for convenience
        /// 
        /// The primary image path.
        [IgnoreDataMember]
        public string PrimaryImagePath
        {
            get { return this.GetImagePath(ImageType.Primary); }
        }
        /// 
        /// Gets or sets the date created.
        /// 
        /// The date created.
        public DateTime DateCreated { get; set; }
        /// 
        /// Gets or sets the date modified.
        /// 
        /// The date modified.
        public DateTime DateModified { get; set; }
        public DateTime DateLastSaved { get; set; }
        /// 
        /// The logger
        /// 
        public static ILogger Logger { get; set; }
        public static ILibraryManager LibraryManager { get; set; }
        public static IServerConfigurationManager ConfigurationManager { get; set; }
        public static IProviderManager ProviderManager { get; set; }
        public static ILocalizationManager LocalizationManager { get; set; }
        public static IItemRepository ItemRepository { get; set; }
        public static IFileSystem FileSystem { get; set; }
        public static IUserDataManager UserDataManager { get; set; }
        /// 
        /// Returns a  that represents this instance.
        /// 
        /// A  that represents this instance.
        public override string ToString()
        {
            return Name;
        }
        /// 
        /// Returns true if this item should not attempt to fetch metadata
        /// 
        /// true if [dont fetch meta]; otherwise, false.
        [Obsolete("Please use IsLocked instead of DontFetchMeta")]
        public bool DontFetchMeta { get; set; }
        [IgnoreDataMember]
        public bool IsLocked
        {
            get
            {
                return DontFetchMeta;
            }
            set
            {
                DontFetchMeta = value;
            }
        }
        public bool IsUnidentified { get; set; }
        [IgnoreDataMember]
        public List Identities { get; set; }
        /// 
        /// Gets or sets the locked fields.
        /// 
        /// The locked fields.
        public List LockedFields { get; set; }
        /// 
        /// Gets the type of the media.
        /// 
        /// The type of the media.
        [IgnoreDataMember]
        public virtual string MediaType
        {
            get
            {
                return null;
            }
        }
        [IgnoreDataMember]
        public virtual IEnumerable PhysicalLocations
        {
            get
            {
                var locationType = LocationType;
                if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
                {
                    return new string[] { };
                }
                return new[] { Path };
            }
        }
        private string _forcedSortName;
        /// 
        /// Gets or sets the name of the forced sort.
        /// 
        /// The name of the forced sort.
        public string ForcedSortName
        {
            get { return _forcedSortName; }
            set { _forcedSortName = value; _sortName = null; }
        }
        private string _sortName;
        /// 
        /// Gets the name of the sort.
        /// 
        /// The name of the sort.
        [IgnoreDataMember]
        public string SortName
        {
            get
            {
                if (!string.IsNullOrEmpty(ForcedSortName))
                {
                    return ForcedSortName;
                }
                return _sortName ?? (_sortName = CreateSortName());
            }
        }
        /// 
        /// Creates the name of the sort.
        /// 
        /// System.String.
        protected virtual string CreateSortName()
        {
            if (Name == null) return null; //some items may not have name filled in properly
            var sortable = Name.Trim().ToLower();
            sortable = ConfigurationManager.Configuration.SortRemoveCharacters.Aggregate(sortable, (current, search) => current.Replace(search.ToLower(), string.Empty));
            sortable = ConfigurationManager.Configuration.SortReplaceCharacters.Aggregate(sortable, (current, search) => current.Replace(search.ToLower(), " "));
            foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
            {
                var searchLower = search.ToLower();
                // Remove from beginning if a space follows
                if (sortable.StartsWith(searchLower + " "))
                {
                    sortable = sortable.Remove(0, searchLower.Length + 1);
                }
                // Remove from middle if surrounded by spaces
                sortable = sortable.Replace(" " + searchLower + " ", " ");
                // Remove from end if followed by a space
                if (sortable.EndsWith(" " + searchLower))
                {
                    sortable = sortable.Remove(sortable.Length - (searchLower.Length + 1));
                }
            }
            return sortable;
        }
        /// 
        /// Gets or sets the parent.
        /// 
        /// The parent.
        [IgnoreDataMember]
        public Folder Parent { get; set; }
        [IgnoreDataMember]
        public IEnumerable Parents
        {
            get
            {
                var parent = Parent;
                while (parent != null)
                {
                    yield return parent;
                    parent = parent.Parent;
                }
            }
        }
        /// 
        /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
        /// 
        /// The premiere date.
        public DateTime? PremiereDate { get; set; }
        /// 
        /// Gets or sets the end date.
        /// 
        /// The end date.
        public DateTime? EndDate { get; set; }
        /// 
        /// Gets or sets the display type of the media.
        /// 
        /// The display type of the media.
        public string DisplayMediaType { get; set; }
        /// 
        /// Gets or sets the official rating.
        /// 
        /// The official rating.
        public string OfficialRating { get; set; }
        /// 
        /// Gets or sets the official rating description.
        /// 
        /// The official rating description.
        public string OfficialRatingDescription { get; set; }
        /// 
        /// Gets or sets the custom rating.
        /// 
        /// The custom rating.
        public string CustomRating { get; set; }
        /// 
        /// Gets or sets the overview.
        /// 
        /// The overview.
        public string Overview { get; set; }
        /// 
        /// Gets or sets the people.
        /// 
        /// The people.
        public List People { get; set; }
        /// 
        /// Gets or sets the studios.
        /// 
        /// The studios.
        public List Studios { get; set; }
        /// 
        /// Gets or sets the genres.
        /// 
        /// The genres.
        public List Genres { get; set; }
        /// 
        /// Gets or sets the home page URL.
        /// 
        /// The home page URL.
        public string HomePageUrl { get; set; }
        /// 
        /// Gets or sets the community rating.
        /// 
        /// The community rating.
        public float? CommunityRating { get; set; }
        /// 
        /// Gets or sets the community rating vote count.
        /// 
        /// The community rating vote count.
        public int? VoteCount { get; set; }
        /// 
        /// Gets or sets the run time ticks.
        /// 
        /// The run time ticks.
        public long? RunTimeTicks { get; set; }
        /// 
        /// Gets or sets the production year.
        /// 
        /// The production year.
        public int? ProductionYear { get; set; }
        /// 
        /// If the item is part of a series, this is it's number in the series.
        /// This could be episode number, album track number, etc.
        /// 
        /// The index number.
        public int? IndexNumber { get; set; }
        /// 
        /// For an episode this could be the season number, or for a song this could be the disc number.
        /// 
        /// The parent index number.
        public int? ParentIndexNumber { get; set; }
        [IgnoreDataMember]
        public virtual string OfficialRatingForComparison
        {
            get { return OfficialRating; }
        }
        [IgnoreDataMember]
        public string CustomRatingForComparison
        {
            get
            {
                if (!string.IsNullOrEmpty(CustomRating))
                {
                    return CustomRating;
                }
                var parent = Parent;
                if (parent != null)
                {
                    return parent.CustomRatingForComparison;
                }
                return null;
            }
        }
        /// 
        /// Gets the play access.
        /// 
        /// The user.
        /// PlayAccess.
        public PlayAccess GetPlayAccess(User user)
        {
            if (!user.Configuration.EnableMediaPlayback)
            {
                return PlayAccess.None;
            }
            return PlayAccess.Full;
        }
        /// 
        /// Loads local trailers from the file system
        /// 
        /// List{Video}.
        private IEnumerable LoadLocalTrailers(List fileSystemChildren, IDirectoryService directoryService)
        {
            var files = fileSystemChildren.OfType()
                .Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
                .ToList();
            // Support plex/xbmc convention
            files.AddRange(fileSystemChildren.OfType()
                .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
                );
            return LibraryManager.ResolvePaths(files, directoryService, null).Select(video =>
            {
                // Try to retrieve it from the db. If we don't find it, use the resolved version
                var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
                if (dbItem != null)
                {
                    video = dbItem;
                }
                return video;
                // Sort them so that the list can be easily compared for changes
            }).OrderBy(i => i.Path).ToList();
        }
        /// 
        /// Loads the theme songs.
        /// 
        /// List{Audio.Audio}.
        private IEnumerable LoadThemeSongs(List fileSystemChildren, IDirectoryService directoryService)
        {
            var files = fileSystemChildren.OfType()
                .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
                .ToList();
            // Support plex/xbmc convention
            files.AddRange(fileSystemChildren.OfType()
                .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
                );
            return LibraryManager.ResolvePaths(files, directoryService, null).Select(audio =>
            {
                // Try to retrieve it from the db. If we don't find it, use the resolved version
                var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
                if (dbItem != null)
                {
                    audio = dbItem;
                }
                return audio;
                // Sort them so that the list can be easily compared for changes
            }).OrderBy(i => i.Path).ToList();
        }
        /// 
        /// Loads the video backdrops.
        /// 
        /// List{Video}.
        private IEnumerable