using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.TV
{
    /// 
    /// Class Season
    /// 
    public class Season : Folder, IHasSeries, IHasLookupInfo
    {
        /// 
        /// Seasons are just containers
        /// 
        /// true if [include in index]; otherwise, false.
        [IgnoreDataMember]
        public override bool IncludeInIndex
        {
            get
            {
                return false;
            }
        }
        [IgnoreDataMember]
        public override bool SupportsAddingToPlaylist
        {
            get { return true; }
        }
        [IgnoreDataMember]
        public override bool IsPreSorted
        {
            get
            {
                return true;
            }
        }
        [IgnoreDataMember]
        public override BaseItem DisplayParent
        {
            get { return Series ?? Parent; }
        }
        /// 
        /// We want to group into our Series
        /// 
        /// true if [group in index]; otherwise, false.
        [IgnoreDataMember]
        public override bool GroupInIndex
        {
            get
            {
                return true;
            }
        }
        /// 
        /// Override this to return the folder that should be used to construct a container
        /// for this item in an index.  GroupInIndex should be true as well.
        /// 
        /// The index container.
        [IgnoreDataMember]
        public override Folder IndexContainer
        {
            get
            {
                return Series;
            }
        }
        // Genre, Rating and Stuido will all be the same
        protected override IEnumerable GetIndexByOptions()
        {
            return new List {            
                {LocalizedStrings.Instance.GetString("NoneDispPref")}, 
                {LocalizedStrings.Instance.GetString("PerformerDispPref")},
                {LocalizedStrings.Instance.GetString("DirectorDispPref")},
                {LocalizedStrings.Instance.GetString("YearDispPref")},
            };
        }
        /// 
        /// Gets the user data key.
        /// 
        /// System.String.
        protected override string CreateUserDataKey()
        {
            if (Series != null)
            {
                var seasonNo = IndexNumber ?? 0;
                return Series.GetUserDataKey() + seasonNo.ToString("000");
            }
            return base.CreateUserDataKey();
        }
        /// 
        /// The _series
        /// 
        private Series _series;
        /// 
        /// This Episode's Series Instance
        /// 
        /// The series.
        [IgnoreDataMember]
        public Series Series
        {
            get { return _series ?? (_series = FindParent()); }
        }
        [IgnoreDataMember]
        public string SeriesPath
        {
            get
            {
                var series = Series;
                if (series != null)
                {
                    return series.Path;
                }
                return System.IO.Path.GetDirectoryName(Path);
            }
        }
        /// 
        /// Our rating comes from our series
        /// 
        [IgnoreDataMember]
        public override string OfficialRatingForComparison
        {
            get
            {
                var series = Series;
                return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
            }
        }
        /// 
        /// Creates the name of the sort.
        /// 
        /// System.String.
        protected override string CreateSortName()
        {
            return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
        }
        [IgnoreDataMember]
        public bool IsMissingSeason
        {
            get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); }
        }
        [IgnoreDataMember]
        public bool IsUnaired
        {
            get { return GetEpisodes().All(i => i.IsUnaired); }
        }
        [IgnoreDataMember]
        public bool IsVirtualUnaired
        {
            get { return LocationType == LocationType.Virtual && IsUnaired; }
        }
        [IgnoreDataMember]
        public bool IsMissingOrVirtualUnaired
        {
            get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); }
        }
        [IgnoreDataMember]
        public bool IsSpecialSeason
        {
            get { return (IndexNumber ?? -1) == 0; }
        }
        /// 
        /// Gets the episodes.
        /// 
        /// The user.
        /// IEnumerable{Episode}.
        public IEnumerable GetEpisodes(User user)
        {
            var config = user.Configuration;
            return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
        }
        public IEnumerable GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
        {
            var episodes = GetRecursiveChildren(user)
                .OfType();
            var series = Series;
            if (IndexNumber.HasValue && series != null)
            {
                return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
            }
            if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
            {
                var seasonNumber = IndexNumber;
                var list = episodes.ToList();
                if (seasonNumber.HasValue)
                {
                    list.AddRange(series.GetRecursiveChildren(user).OfType()
                        .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
                }
                else
                {
                    list.AddRange(series.GetRecursiveChildren(user).OfType()
                        .Where(i => !i.ParentIndexNumber.HasValue));
                }
                episodes = list.DistinctBy(i => i.Id);
            }
            
            if (!includeMissingEpisodes)
            {
                episodes = episodes.Where(i => !i.IsMissingEpisode);
            }
            if (!includeVirtualUnairedEpisodes)
            {
                episodes = episodes.Where(i => !i.IsVirtualUnaired);
            }
            return LibraryManager
                .Sort(episodes, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
                .Cast();
        }
        private IEnumerable GetEpisodes()
        {
            var episodes = GetRecursiveChildren().OfType();
            var series = Series;
            if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
            {
                var seasonNumber = IndexNumber;
                var list = episodes.ToList();
                if (seasonNumber.HasValue)
                {
                    list.AddRange(series.GetRecursiveChildren().OfType()
                        .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
                }
                else
                {
                    list.AddRange(series.GetRecursiveChildren().OfType()
                        .Where(i => !i.ParentIndexNumber.HasValue));
                }
                episodes = list.DistinctBy(i => i.Id);
            }
            return episodes;
        }
        public override IEnumerable GetChildren(User user, bool includeLinkedChildren)
        {
            return GetEpisodes(user);
        }
        protected override bool GetBlockUnratedValue(UserPolicy config)
        {
            // Don't block. Let either the entire series rating or episode rating determine it
            return false;
        }
        [IgnoreDataMember]
        public string SeriesName
        {
            get
            {
                var series = Series;
                return series == null ? null : series.Name;
            }
        }
        /// 
        /// Gets the lookup information.
        /// 
        /// SeasonInfo.
        public SeasonInfo GetLookupInfo()
        {
            var id = GetItemLookupInfo();
            var series = Series;
            if (series != null)
            {
                id.SeriesProviderIds = series.ProviderIds;
                id.AnimeSeriesIndex = series.AnimeSeriesIndex;
            }
            return id;
        }
        /// 
        /// This is called before any metadata refresh and returns true or false indicating if changes were made
        /// 
        /// true if XXXX, false otherwise.
        public override bool BeforeMetadataRefresh()
        {
            var hasChanges = base.BeforeMetadataRefresh();
            var locationType = LocationType;
            if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
            {
                if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
                {
                    IndexNumber = IndexNumber ?? LibraryManager.GetSeasonNumberFromPath(Path);
                    // If a change was made record it
                    if (IndexNumber.HasValue)
                    {
                        hasChanges = true;
                    }
                }
            }
            return hasChanges;
        }
    }
}