using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using BrightErrorsLed;
using Emgu.CV.Structure;
using Emgu.CV;
using Emgu.Util.TypeEnum;
using Emgu.CV.ML;
using Emgu.CV.Cuda;
using System.IO;
using System.Threading;

namespace BrightErrorsLed
{
    // Main Methods
    class Controller
    {
        private string saveFile;
        private Rectangle resolution = Screen.PrimaryScreen.Bounds;
        public Controller()
        {
        }

        public Bitmap _resize(Image image, int width, int height)
        {
            Bitmap newImage = new Bitmap(width, height);

            newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
            using (Graphics gr = Graphics.FromImage(newImage))
            {
                gr.DrawImage(image, new Rectangle(0, 0, width, height));
            }
            GC.Collect();
            return newImage;
        }

        public async Task<Image<Bgr, Byte>> SetLineBrightness(int lightness, Bitmap bmap, Point[] body)
        {
            Image<Hls, Byte> currentBmap = new Image<Hls, byte>(bmap);
            Image<Hls, Byte> newBmap = new Image<Hls, byte>(bmap);
            var absDiff = await Task.Run(() =>
            {
                for (int i = 0; i < body.Length; i++)
                {
                    var c = newBmap[body[i].Y, body[i].X];
                    c.Lightness += lightness;
                    newBmap[body[i].Y, body[i].X] = c;
                }
                var diff = currentBmap.Convert<Bgr, byte>().AbsDiff(newBmap.Convert<Bgr, byte>());
                return diff;
            });
            return absDiff;
        }
        private async Task<Image<Hls, Byte>> FindBitmapChanges(Image<Hls, Byte> org, Image<Hls, Byte> changed, Image<Hls, Byte> newBitmap, Nullable<PixelChanges>[] changes)
        {
            await Task.Run(() =>
            {
                for (int i = 0; i < changes.Count(); i++)
                {
                    if (changes[i] == null)
                    {
                        return newBitmap;
                    }
                    var n = newBitmap[changes[i].Value.Y, changes[i].Value.X];
                    n.Lightness -= org[changes[i].Value.Y, changes[i].Value.X].Lightness - changed[changes[i].Value.Y, changes[i].Value.X].Lightness;
                    newBitmap[changes[i].Value.Y, changes[i].Value.X] = n;

                }
                return newBitmap;
            });
            return newBitmap;
        }
        public static async Task<PixelChanges?[]> FindChanged(Bitmap org, Bitmap changed)
        {
            Nullable<PixelChanges>[] changedPixels = new Nullable<PixelChanges>[Screen.PrimaryScreen.Bounds.Width * Screen.PrimaryScreen.Bounds.Height];
            await Task.Run(() =>
            {
                Image<Hls, Byte> _org = new Image<Hls, byte>(org);
                Image<Hls, Byte> _changed = new Image<Hls, byte>(changed);
                var c = Color.FromArgb(0, 0, 0);
                int n = 0;
                var diff = _org.Cmp(_changed, Emgu.CV.CvEnum.CmpType.NotEqual);
                var diffBtm = diff.Bitmap;
                for (int i = 1; i < org.Height; i++)
                    for (int j = 1; j < org.Width; j++)
                    {
                        if (diffBtm.GetPixel(j, i) != c)
                        {
                            changedPixels[n] = new PixelChanges(j, i);
                            n++;
                        }
                    }
                if (n != 0)
                    return changedPixels;
                else return null;
            });
            return changedPixels;
        }

        public async Task UpChangesToVideoFile(IProgress<int> progress, Emgu.CV.CvEnum.Inter iter, string codec, bool change = false)
        {
            var t = new Thread(() =>
            {

                VideoWriter VW;
                Capture Video;

                GC.Collect();
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "Avi|*.avi|Wmv|*.wmv|Mp4|*.mp4";
                ofd.Title = "Open an Video File";
                ofd.ShowDialog();
                string image = ofd.FileName;
                if (image != "")
                {
                    Video = new Capture(image);
                    SaveFileDialog sfd = new SaveFileDialog();
                    MessageBox.Show("Please choose directory to save your new video");
                    if (codec == "WMV2")
                        sfd.Filter = "Windows Media Video 8|*.wmv";
                    else
                        sfd.Filter = "Avi|*.avi";
                    sfd.Title = ("Name your video and choose directory to save it please");
                    sfd.ShowDialog();
                    if (sfd.FileName == "")
                        return;
                    char[] fourcc = null;
                    if (codec != null && codec.Length == 4)
                        fourcc = codec.ToArray();
                    if (fourcc != null && fourcc.Count() == 4)
                        VW = new VideoWriter(sfd.FileName, VideoWriter.Fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]), Convert.ToInt32(Video.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps)), new Size(resolution.Width, resolution.Height), true);
                    else
                        VW = new VideoWriter(sfd.FileName, VideoWriter.Fourcc('D', 'I', 'V', 'X'), Convert.ToInt32(Video.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.Fps)), new Size(resolution.Width, resolution.Height), true);
                    saveFile = sfd.FileName;
                    if (image == saveFile)
                    {
                        MessageBox.Show("Source and target video can't be the same ones");
                        return;
                    }
                }
                else
                    return;


                VW = RemakeVideoAsync(Video, VW, progress, iter);

                if (!BrightErrosLed.Cancel)
                {
                    VW.Dispose();
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    MessageBox.Show("Video is succesfully remastered and saved on disk in directory: " + saveFile);
                }
                return;
            });
            t.SetApartmentState(ApartmentState.STA);
            await Task.Run(() =>
            {
                t.Start();
                t.Join();
            });
        }


        private VideoWriter RemakeVideoAsync(Capture Video, VideoWriter VW, IProgress<int> progress, Emgu.CV.CvEnum.Inter iter)
        {
            BrightErrosLed.Remastering = true;
            BrightErrosLed.Cancel = false;
            var diff = BrightErrosLed.MyNewBitmap.AbsDiff(BrightErrosLed.MyOriginalBitmap);
            for (int i = 1; i < (Video.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameCount) - 1); i++)
            {
                var NewFrame = (Video.QueryFrame().ToImage<Bgr, Byte>());

                NewFrame = NewFrame.Resize(resolution.Width, resolution.Height, iter);
                if (BrightErrosLed.LightnessIntensity >= 0)
                    NewFrame = (NewFrame + diff);
                else
                    NewFrame = (NewFrame - diff);
                VW.Write(NewFrame.Mat);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                progress.Report(Convert.ToInt32((i / (Video.GetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameCount) - 1)) * 100));
                if (BrightErrosLed.Cancel)
                {
                    break;
                }
            }
            BrightErrosLed.Remastering = false;
            return VW;
        }
    }


}

