using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Session;

namespace MediaBrowser.Controller.SyncPlay
{
    /// <summary>
    /// Class GroupInfo.
    /// </summary>
    /// <remarks>
    /// Class is not thread-safe, external locking is required when accessing methods.
    /// </remarks>
    public class GroupInfo
    {
        /// <summary>
        /// Gets the default ping value used for sessions.
        /// </summary>
        public long DefaultPing { get; } = 500;

        /// <summary>
        /// Gets or sets the group identifier.
        /// </summary>
        /// <value>The group identifier.</value>
        public Guid GroupId { get; } = Guid.NewGuid();

        /// <summary>
        /// Gets or sets the playing item.
        /// </summary>
        /// <value>The playing item.</value>
        public BaseItem PlayingItem { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether playback is paused.
        /// </summary>
        /// <value>Playback is paused.</value>
        public bool IsPaused { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether there are position ticks.
        /// </summary>
        /// <value>The position ticks.</value>
        public long PositionTicks { get; set; }

        /// <summary>
        /// Gets or sets the last activity.
        /// </summary>
        /// <value>The last activity.</value>
        public DateTime LastActivity { get; set; }

        /// <summary>
        /// Gets the participants.
        /// </summary>
        /// <value>The participants, or members of the group.</value>
        public Dictionary<string, GroupMember> Participants { get; } =
            new Dictionary<string, GroupMember>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// Checks if a session is in this group.
        /// </summary>
        /// <value><c>true</c> if the session is in this group; <c>false</c> otherwise.</value>
        public bool ContainsSession(string sessionId)
        {
            return Participants.ContainsKey(sessionId);
        }

        /// <summary>
        /// Adds the session to the group.
        /// </summary>
        /// <param name="session">The session.</param>
        public void AddSession(SessionInfo session)
        {
            if (ContainsSession(session.Id))
            {
                return;
            }

            var member = new GroupMember();
            member.Session = session;
            member.Ping = DefaultPing;
            member.IsBuffering = false;
            Participants[session.Id] = member;
        }

        /// <summary>
        /// Removes the session from the group.
        /// </summary>
        /// <param name="session">The session.</param>
        public void RemoveSession(SessionInfo session)
        {
            if (!ContainsSession(session.Id))
            {
                return;
            }

            Participants.Remove(session.Id, out _);
        }

        /// <summary>
        /// Updates the ping of a session.
        /// </summary>
        /// <param name="session">The session.</param>
        /// <param name="ping">The ping.</param>
        public void UpdatePing(SessionInfo session, long ping)
        {
            if (!ContainsSession(session.Id))
            {
                return;
            }

            Participants[session.Id].Ping = ping;
        }

        /// <summary>
        /// Gets the highest ping in the group.
        /// </summary>
        /// <value name="session">The highest ping in the group.</value>
        public long GetHighestPing()
        {
            long max = long.MinValue;
            foreach (var session in Participants.Values)
            {
                max = Math.Max(max, session.Ping);
            }

            return max;
        }

        /// <summary>
        /// Sets the session's buffering state.
        /// </summary>
        /// <param name="session">The session.</param>
        /// <param name="isBuffering">The state.</param>
        public void SetBuffering(SessionInfo session, bool isBuffering)
        {
            if (!ContainsSession(session.Id))
            {
                return;
            }

            Participants[session.Id].IsBuffering = isBuffering;
        }

        /// <summary>
        /// Gets the group buffering state.
        /// </summary>
        /// <value><c>true</c> if there is a session buffering in the group; <c>false</c> otherwise.</value>
        public bool IsBuffering()
        {
            foreach (var session in Participants.Values)
            {
                if (session.IsBuffering)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Checks if the group is empty.
        /// </summary>
        /// <value><c>true</c> if the group is empty; <c>false</c> otherwise.</value>
        public bool IsEmpty()
        {
            return Participants.Count == 0;
        }
    }
}