using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AForge.Video;
using AForge.Video.DirectShow;
using log4net;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using OpenCvSharp.Extensions;

namespace YSoft.Rqamfp.Camera
{
    public static class Camera
    {
        private static VideoCaptureDevice _video;
        private static Mat _image;
        private static int _actualFrame = 1;
        public static int CameraIndex;
        public static string ImageProcessingServer;
        public static List<Point2f> CameraCalibration;
        public static int MfdId;
        public static Mat LastImage;
        public static bool Calibrate = false;
        private static readonly ILog Logger = LogManager.GetLogger(typeof(Camera));

        public static Mat Snap() {
            var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            _video = new VideoCaptureDevice(videoDevices[CameraIndex].MonikerString);
            var camera = _video.VideoCapabilities
                .Select((Value, Index) => new {Value = Value, Index})
                .SingleOrDefault(o => o.Value.FrameSize.Height == 1080 && o.Value.FrameSize.Width == 1920);
            if (camera == null) {
                Logger.Warn("Selected camera either does not exists or does not supports full HD snapping, select another camera");
                return null;
            }
            _video.VideoResolution = _video.VideoCapabilities[camera.Index];
            _video.NewFrame += (VideoOnNewFrame);
            _video.Start();
            _video.WaitForStop();
            _video.NewFrame -= (VideoOnNewFrame);
            _video.Stop();
            _video = null;
            _actualFrame = 1;
            return _image;
        }

        private static void VideoOnNewFrame(object sender, NewFrameEventArgs eventArgs) {
            if (_actualFrame < 10) {
                using (Bitmap bitmap = (Bitmap) eventArgs.Frame.Clone()) {
                    Mat mat = bitmap.ToMat();
                    if (_actualFrame == 1) {
                        _image = new Mat();
                        _image = mat;
                    }
                    else
                        Cv2.AddWeighted(_image, 0.5, mat, 0.5, 0, _image);
                    _actualFrame++;
                }
            }
            else {
                _video.SignalToStop();
                _actualFrame = 1;
            }
        }
        
        public static int GetCameraCount() {
            return new FilterInfoCollection(FilterCategory.VideoInputDevice).Count;
        }

        public static void SaveSnappedImage(string identifier) {
            var time = new Stopwatch();
            const string dir = @"C:\RQA\imgs\error\";
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            var path = dir + identifier + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".png";
            Snap().SaveImage(path, new PngEncodingParam(100));
            time.Start();
            // This while waits for file to physicaly exist on the disk, otherwise mat object is disposed before image is written which results in exception
            while (!File.Exists(path) && time.Elapsed.Seconds < 10) {
                Task.Delay(500).Wait();
            }
        }
    }
}