using System;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Model.Entities;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
{
    /// 
    /// Class IndexFolder
    /// 
    public class IndexFolder : Folder
    {
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The parent.
        /// The shadow.
        /// The children.
        /// Name of the index.
        /// if set to true [group contents].
        public IndexFolder(Folder parent, BaseItem shadow, IEnumerable children, string indexName, bool groupContents = true)
        {
            ChildSource = children;
            ShadowItem = shadow;
            GroupContents = groupContents;
            if (shadow == null)
            {
                Name = ForcedSortName = "";
            }
            else
            {
                SetShadowValues();
            }
            Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder));
            IndexName = indexName;
            Parent = parent;
        }
        /// 
        /// Resets the parent.
        /// 
        /// The parent.
        public void ResetParent(Folder parent)
        {
            Parent = parent;
            Id = (parent.Id.ToString() + Name).GetMBId(typeof(IndexFolder));
        }
        /// 
        /// Override this to true if class should be grouped under a container in indicies
        /// The container class should be defined via IndexContainer
        /// 
        /// true if [group in index]; otherwise, false.
        [IgnoreDataMember]
        public override bool GroupInIndex
        {
            get
            {
                return ShadowItem != null && ShadowItem.GroupInIndex;
            }
        }
        public override LocationType LocationType
        {
            get
            {
                return LocationType.Virtual;
            }
        }
        /// 
        /// 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 ShadowItem != null ? ShadowItem.IndexContainer : new IndexFolder(this, null, null, "", false); }
        }
        /// 
        /// Gets or sets a value indicating whether [group contents].
        /// 
        /// true if [group contents]; otherwise, false.
        protected bool GroupContents { get; set; }
        /// 
        /// Gets or sets the child source.
        /// 
        /// The child source.
        protected IEnumerable ChildSource { get; set; }
        /// 
        /// Gets or sets our children.
        /// 
        /// Our children.
        protected ConcurrentBag OurChildren { get; set; }
        /// 
        /// Gets the name of the index.
        /// 
        /// The name of the index.
        public string IndexName { get; private set; }
        /// 
        /// Override to return the children defined to us when we were created
        /// 
        /// The actual children.
        protected override ConcurrentDictionary LoadChildren()
        {
            var originalChildSource = ChildSource.ToList();
            var kids = originalChildSource;
            if (GroupContents)
            {
                // Recursively group up the chain
                var group = true;
                var isSubsequentLoop = false;
                while (group)
                {
                    kids = isSubsequentLoop || kids.Any(i => i.GroupInIndex)
                               ? GroupedSource(kids).ToList()
                               : originalChildSource;
                    group = kids.Any(i => i.GroupInIndex);
                    isSubsequentLoop = true;
                }
            }
            // Now - since we built the index grouping from the bottom up - we now need to properly set Parents from the top down
            SetParents(this, kids.OfType());
            return new ConcurrentDictionary(kids.ToDictionary(i => i.Id));
        }
        /// 
        /// Sets the parents.
        /// 
        /// The parent.
        /// The kids.
        private void SetParents(Folder parent, IEnumerable kids)
        {
            foreach (var child in kids)
            {
                child.ResetParent(parent);
                child.SetParents(child, child.Children.OfType());
            }
        }
        /// 
        /// Groupeds the source.
        /// 
        /// The source.
        /// IEnumerable{BaseItem}.
        protected IEnumerable GroupedSource(IEnumerable source)
        {
            return source.GroupBy(i => i.IndexContainer).Select(container => new IndexFolder(this, container.Key, container, null, false));
        }
        /// 
        /// The item we are shadowing as a folder (Genre, Actor, etc.)
        /// We inherit the images and other meta from this item
        /// 
        /// The shadow item.
        protected BaseItem ShadowItem { get; set; }
        /// 
        /// Sets the shadow values.
        /// 
        protected void SetShadowValues()
        {
            if (ShadowItem != null)
            {
                Name = ShadowItem.Name;
                ForcedSortName = ShadowItem.SortName;
                Genres = ShadowItem.Genres;
                Studios = ShadowItem.Studios;
                OfficialRating = ShadowItem.OfficialRating;
                BackdropImagePaths = ShadowItem.BackdropImagePaths;
                Images = ShadowItem.Images;
                Overview = ShadowItem.Overview;
                DisplayMediaType = ShadowItem.GetType().Name;
            }
        }
        /// 
        /// Overrides the base implementation to refresh metadata for local trailers
        /// 
        /// The cancellation token.
        /// if set to true [is new item].
        /// if set to true [force].
        /// if set to true [allow slow providers].
        /// if set to true [reset resolve args].
        /// Task{System.Boolean}.
        public override Task RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true)
        {
            // We should never get in here since these are not part of the library
            return Task.FromResult(false);
        }
    }
}