using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.Models.VideoDtos;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.MediaInfo;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers
{
    /// 
    /// The universal audio controller.
    /// 
    [Route("")]
    public class UniversalAudioController : BaseJellyfinApiController
    {
        private readonly IAuthorizationContext _authorizationContext;
        private readonly MediaInfoController _mediaInfoController;
        private readonly DynamicHlsController _dynamicHlsController;
        private readonly AudioController _audioController;
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// Instance of the  interface.
        /// Instance of the .
        /// Instance of the .
        /// Instance of the .
        public UniversalAudioController(
            IAuthorizationContext authorizationContext,
            MediaInfoController mediaInfoController,
            DynamicHlsController dynamicHlsController,
            AudioController audioController)
        {
            _authorizationContext = authorizationContext;
            _mediaInfoController = mediaInfoController;
            _dynamicHlsController = dynamicHlsController;
            _audioController = audioController;
        }
        /// 
        /// Gets an audio stream.
        /// 
        /// The item id.
        /// Optional. The audio container.
        /// The media version id, if playing an alternate version.
        /// The device id of the client requesting. Used to stop encoding processes when needed.
        /// Optional. The user id.
        /// Optional. The audio codec to transcode to.
        /// Optional. The maximum number of audio channels.
        /// Optional. The number of how many audio channels to transcode to.
        /// Optional. The maximum streaming bitrate.
        /// Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.
        /// Optional. The container to transcode to.
        /// Optional. The transcoding protocol.
        /// Optional. The maximum audio sample rate.
        /// Optional. The maximum audio bit depth.
        /// Optional. Whether to enable remote media.
        /// Optional. Whether to break on non key frames.
        /// Whether to enable redirection. Defaults to true.
        /// Audio stream returned.
        /// Redirected to remote audio stream.
        /// A  containing the audio file.
        [HttpGet("Audio/{itemId}/universal")]
        [HttpGet("Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")]
        [HttpHead("Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")]
        [HttpHead("Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")]
        [Authorize(Policy = Policies.DefaultAuthorization)]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status302Found)]
        public async Task GetUniversalAudioStream(
            [FromRoute] Guid itemId,
            [FromRoute] string? container,
            [FromQuery] string? mediaSourceId,
            [FromQuery] string? deviceId,
            [FromQuery] Guid? userId,
            [FromQuery] string? audioCodec,
            [FromQuery] int? maxAudioChannels,
            [FromQuery] int? transcodingAudioChannels,
            [FromQuery] long? maxStreamingBitrate,
            [FromQuery] long? startTimeTicks,
            [FromQuery] string? transcodingContainer,
            [FromQuery] string? transcodingProtocol,
            [FromQuery] int? maxAudioSampleRate,
            [FromQuery] int? maxAudioBitDepth,
            [FromQuery] bool? enableRemoteMedia,
            [FromQuery] bool breakOnNonKeyFrames,
            [FromQuery] bool enableRedirection = true)
        {
            bool isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
            var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels);
            _authorizationContext.GetAuthorizationInfo(Request).DeviceId = deviceId;
            var playbackInfoResult = await _mediaInfoController.GetPostedPlaybackInfo(
                itemId,
                userId,
                maxStreamingBitrate,
                startTimeTicks,
                null,
                null,
                maxAudioChannels,
                mediaSourceId,
                null,
                new DeviceProfileDto { DeviceProfile = deviceProfile })
                .ConfigureAwait(false);
            var mediaSource = playbackInfoResult.Value.MediaSources[0];
            if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
            {
                if (enableRedirection)
                {
                    if (mediaSource.IsRemote && enableRemoteMedia.HasValue && enableRemoteMedia.Value)
                    {
                        return Redirect(mediaSource.Path);
                    }
                }
            }
            var isStatic = mediaSource.SupportsDirectStream;
            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
            {
                var transcodingProfile = deviceProfile.TranscodingProfiles[0];
                // hls segment container can only be mpegts or fmp4 per ffmpeg documentation
                // TODO: remove this when we switch back to the segment muxer
                var supportedHlsContainers = new[] { "mpegts", "fmp4" };
                if (isHeadRequest)
                {
                    _dynamicHlsController.Request.Method = HttpMethod.Head.Method;
                }
                return await _dynamicHlsController.GetMasterHlsAudioPlaylist(
                    itemId,
                    ".m3u8",
                    isStatic,
                    null,
                    null,
                    null,
                    playbackInfoResult.Value.PlaySessionId,
                    // fallback to mpegts if device reports some weird value unsupported by hls
                    Array.Exists(supportedHlsContainers, element => element == transcodingContainer) ? transcodingContainer : "mpegts",
                    null,
                    null,
                    mediaSource.Id,
                    deviceId,
                    transcodingProfile.AudioCodec,
                    null,
                    null,
                    null,
                    transcodingProfile.BreakOnNonKeyFrames,
                    maxAudioSampleRate,
                    maxAudioBitDepth,
                    null,
                    isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)),
                    maxAudioChannels,
                    null,
                    null,
                    null,
                    null,
                    null,
                    startTimeTicks,
                    null,
                    null,
                    null,
                    null,
                    SubtitleDeliveryMethod.Hls,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
                    null,
                    null,
                    EncodingContext.Static,
                    new Dictionary())
                    .ConfigureAwait(false);
            }
            else
            {
                if (isHeadRequest)
                {
                    _audioController.Request.Method = HttpMethod.Head.Method;
                }
                return await _audioController.GetAudioStream(
                    itemId,
                    isStatic ? null : ("." + mediaSource.TranscodingContainer),
                    isStatic,
                    null,
                    null,
                    null,
                    playbackInfoResult.Value.PlaySessionId,
                    null,
                    null,
                    null,
                    mediaSource.Id,
                    deviceId,
                    audioCodec,
                    null,
                    null,
                    null,
                    breakOnNonKeyFrames,
                    maxAudioSampleRate,
                    maxAudioBitDepth,
                    isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)),
                    null,
                    maxAudioChannels,
                    null,
                    null,
                    null,
                    null,
                    null,
                    startTimeTicks,
                    null,
                    null,
                    null,
                    null,
                    SubtitleDeliveryMethod.Embed,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
                    null,
                    null,
                    null,
                    null)
                    .ConfigureAwait(false);
            }
        }
        private DeviceProfile GetDeviceProfile(
            string? container,
            string? transcodingContainer,
            string? audioCodec,
            string? transcodingProtocol,
            bool? breakOnNonKeyFrames,
            int? transcodingAudioChannels,
            int? maxAudioSampleRate,
            int? maxAudioBitDepth,
            int? maxAudioChannels)
        {
            var deviceProfile = new DeviceProfile();
            var directPlayProfiles = new List();
            var containers = RequestHelpers.Split(container, ',', true);
            foreach (var cont in containers)
            {
                var parts = RequestHelpers.Split(cont, ',', true);
                var audioCodecs = parts.Length == 1 ? null : string.Join(",", parts.Skip(1).ToArray());
                directPlayProfiles.Add(new DirectPlayProfile { Type = DlnaProfileType.Audio, Container = parts[0], AudioCodec = audioCodecs });
            }
            deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
            deviceProfile.TranscodingProfiles = new[]
            {
                new TranscodingProfile
                {
                    Type = DlnaProfileType.Audio,
                    Context = EncodingContext.Streaming,
                    Container = transcodingContainer,
                    AudioCodec = audioCodec,
                    Protocol = transcodingProtocol,
                    BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
                    MaxAudioChannels = transcodingAudioChannels?.ToString(CultureInfo.InvariantCulture)
                }
            };
            var codecProfiles = new List();
            var conditions = new List();
            if (maxAudioSampleRate.HasValue)
            {
                // codec profile
                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioSampleRate, Value = maxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) });
            }
            if (maxAudioBitDepth.HasValue)
            {
                // codec profile
                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioBitDepth, Value = maxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture) });
            }
            if (maxAudioChannels.HasValue)
            {
                // codec profile
                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioChannels, Value = maxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) });
            }
            if (conditions.Count > 0)
            {
                // codec profile
                codecProfiles.Add(new CodecProfile { Type = CodecType.Audio, Container = container, Conditions = conditions.ToArray() });
            }
            deviceProfile.CodecProfiles = codecProfiles.ToArray();
            return deviceProfile;
        }
    }
}