using DirectShow;
using Sonic;
using System;
using System.Linq;
using System.Windows.Forms;
using VideoPlayLib.KnownGuid;

namespace VideoPlayLib
{
    public class VideoPlay: IDisposable
    {
        //---- fields
        private readonly Graph _gWrap;

        private readonly DSFilter _audioRender;

        private readonly DSPin[] _arrAudioPins;

        private DSPin _currentAudioOutPin;

        private VideoInControl[] _vics;

        //---- ctor

        public VideoPlay(string mediaFile, params Control[] videoContainers)
        {
            _gWrap = new Graph();

            _audioRender = new DSFilter(AudioRender.DirectSound);
            _gWrap.AddFilter(_audioRender, "audio");

            var dsfSrc = _gWrap.AddSourceFilter(mediaFile, "src");

            if (dsfSrc.Output.Count == 0)
            {
                throw new Exception("Not found any output pin from source!");
            }

            if (dsfSrc.Output.Count == 1 && dsfSrc.OutputPin.MediaTypes.Any(p => p.majorType == MediaType.Stream))
            {
                //we got a Stream, should find a spliter
                var pinSource = dsfSrc.OutputPin;
                var guid = pinSource.MediaTypes.Where(p => p.subType != Guid.Empty).Select(p => p.subType).FirstOrDefault();

                DSFilter dsfSplit;

                if (guid == MediaSubType.Avi)
                    dsfSplit = new DSFilter(Spliter.Avi);
                else if (guid == MediaSubTypeGuids.Mp4)
                    dsfSplit = new DSFilter(Spliter.Mp4);
                else if (guid == MediaSubTypeGuids.Mpg)
                    dsfSplit = new DSFilter(Spliter.Mpeg);
                else if (guid == MediaSubTypeGuids.Flv)
                    dsfSplit = new DSFilter(Spliter.Flv);
                else
                {
                    throw new Exception("Not found any support MediaSubType!");
                }

                _gWrap.AddFilter(dsfSplit, "spliter");
                _gWrap.Connect(pinSource, dsfSplit.InputPin);

                dsfSrc = dsfSplit;
            }

            _arrAudioPins = dsfSrc.Output.Where(p => p.MediaTypes.Any(q => q.majorType == MediaType.Audio)).ToArray();
            var arrVideoPins = dsfSrc.Output.Where(p => p.MediaTypes.Any(q => q.majorType == MediaType.Video)).ToArray();

            if (_arrAudioPins.Any())
            {
                _gWrap.Connect(_currentAudioOutPin = _arrAudioPins.First(), _audioRender.InputPin);
            }

            if (!arrVideoPins.Any() || !videoContainers.Any())
                return;

            if (videoContainers.Length == 1)
            {
                var videoRender = new DSFilter(VideoRender.MadVr);

                _gWrap.AddFilter(videoRender, "video");
                _gWrap.Connect(arrVideoPins.First(), videoRender.InputPin);

                _vics = new[]
                {
                   new  VideoInControl(videoRender, videoContainers.First(), false)
                };
            }
            else //use tee
            {
                _vics = new VideoInControl[videoContainers.Length];

                //define a tee
                var dsfInfTee = new DSFilter(Misc.InfTee);
                _gWrap.AddFilter(dsfInfTee, "infTee");

                //connect input pin of tee , from source
                _gWrap.Connect(arrVideoPins.First(), dsfInfTee.InputPin);

                //connect output pin of tee , to evety render
                for (var i = 0; i < videoContainers.Length; i++)
                {
                    var videoRender = new DSFilter(VideoRender.MadVr);
                    _gWrap.AddFilter(videoRender, "video" + i);

                    //FIXME: 某些情况会出错 TODO: 让tee在解码滤镜之后
                    _gWrap.Connect(dsfInfTee.Output[i], videoRender.InputPin);

                    _vics[i] = new VideoInControl(videoRender, videoContainers[i], false);
                }
            }
        }

        //--- members

        public string[] Audios { get { return _arrAudioPins.Select(p => p.Name).ToArray(); } }

        public void SetAudio(int index)
        {
            var s = _gWrap.GetState();
            if (s == FilterState.Running)
                _gWrap.Stop();

            if (_audioRender.InputPin.ConnectedTo.Filter.Value != _currentAudioOutPin.Filter.Value)
            {
                _gWrap.RemoveFilter(_audioRender.InputPin.ConnectedTo.Filter);
            }
            else
            {
                _gWrap.Disconnect(_audioRender.InputPin);
                _gWrap.Disconnect(_currentAudioOutPin);
            }

            _gWrap.Connect(_arrAudioPins[index], _audioRender.InputPin);

            if (s == FilterState.Running)
                _gWrap.Run();

            _currentAudioOutPin = _arrAudioPins[index];
        }

        public void Play()
        {
            _gWrap.Run();
        }

        public void Pause()
        {
            _gWrap.Pause();
        }

        public void Dispose()
        {
            _gWrap.Abort();
        }

        public double Duration { get { return _gWrap.Duration; } }

        public double CurrentPosition
        {
            get { return _gWrap.CurrentPosition; }
            set { _gWrap.CurrentPosition = value; }
        }
    }
}