using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Drawing;
using System.Threading;
using Avi;

namespace TwoCamWPF.Helpers
{

    /// <summary>
    /// Связывает поток для записи, устройство ввода и всё это обрамляет очередью с буффером
    /// </summary>
    class SingleVideoBuffer : IDisposable
    {
        VideoOutput device;
        VideoStreamWrite _stream = null;
        MagicQueue<Bitmap> blackBox;
        //  public OneCameraSettings settings;
        int maxsize;
        Thread thr;
        ManualResetEvent closeEvent = new ManualResetEvent(false);
        public SingleVideoBuffer(VideoOutput device, VideoStreamWrite stream, MagicQueue<Bitmap> blackBox, int maxsize)
        {
            this.device = device;
            this.blackBox = blackBox;
            this.stream = stream;
            this.maxsize = maxsize;
            device.Subscribe(blackBox, (bmp) => blackBox.Enqueue((Bitmap)bmp.Clone()));
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "   Созадется новая связка");
            thr = new Thread(new ThreadStart(Save)) { Name = "Thread writer " + stream.Name };
            thr.Start();
        }

        public VideoStreamWrite stream
        {
            get { return _stream; }
            set
            {
                //if (value == null)
                //{
                //    device.Unsubscribe(blackBox);
                //}
                //else
                //    device.Subscribe(blackBox, (bmp) => blackBox.Enqueue((Bitmap)bmp.Clone()));

                _stream = value;

            }

        }

        public SingleVideoBuffer(SingleVideoBuffer oldobject, VideoStreamWrite stream)
        {
            this.device = oldobject.device;
            this.blackBox = oldobject.blackBox;
            this.stream = stream;
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "   Новый поток готов. Создается связка. В буфере " + oldobject.blackBox.Count + " кадров");
            this.maxsize = oldobject.maxsize;
            thr = new Thread(new ThreadStart(Save)) { Name = "Thread writer " + stream.Name };
            thr.Start();
        }

        void Save()
        {
            //while (true)
            //{

            //    if (closeEvent.WaitOne(20))
            //    {
            //        blackBox.Dispose();
            //        return;
            //    }
            //    if (stream == null)
            //    {
            //        Thread.Sleep(100);
            //        continue;
            //    }
            //    if (stream.position < maxsize)
            //    {
            //        if (blackBox.CanWrite())
            //        {
            //            MagicQueue<Bitmap>.GetMany tmp = blackBox.Get(maxsize - stream.position);
            //            stream.AddFrame(tmp.elem, tmp.count);
            //            // stream.AddFrame(blackBox.Dequeue());
            //        }
            //    }
            //}
            while (stream.position < maxsize)
            {
                if (blackBox.CanWrite())
                {
                    var tmp = blackBox.Get(maxsize - stream.position);
                    stream.AddFrame(tmp.elem, tmp.count);
                    // stream.AddFrame(blackBox.Dequeue());
                }
                else
                {
                    if (closeEvent.WaitOne(20))
                    {
                        blackBox.Dispose();
                        return;
                    }
                }
            }
        }

        public bool IsFinished()
        {
            //if (closeEvent.WaitOne(20)) //Если мы хотим завершить запись
            //    return true;
            //else
            //    if (stream.position >= maxsize)
            //        return true; //Если файл дописали до конца
            //    else if (blackBox.CanWrite() == false)
            //        return true;
            //    else
            //        return false;
            //Если нечего записывать
            if (stream.position >= maxsize)
                return true; //Если файл дописали до конца
            else if (blackBox.CanWrite() == false)
                //Если нечего записывать
                if (closeEvent.WaitOne(20)) //Если мы хотим завершить запись
                    return true;
                else
                    return false;
            else
                return false;
        }

        public void Close()
        {
            closeEvent.Set();
        }

        public void Dispose()
        {
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "   Удаляем связку");
            device.Unsubscribe(blackBox);
            Close();
        }
    }


    class QueueElem<T>
    {
        public T elem;
        public int count;
        public QueueElem(T elem)
        {
            this.elem = elem;
            count = 0;
        }
    }


    class MagicQueue<T> : Queue<QueueElem<T>>, IDisposable where T : class, IDisposable
    {


        Timer t;
        public volatile int count = 0;
        long maxcount;

        public MagicQueue(T Frame, int time, long maxsize, int onesize)
        {
            this.maxcount = maxsize / onesize;
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), " Максимальное количество элементов " + maxcount);
            lock (Frame)
            {
                Enqueue(Frame);
            }
            t = new Timer((_) =>
            {
                lock (this)
                {
                    if (base.Count > 0)
                        this.Last().count++; ++count;
                }
            }, null, 0, time);
        }

        public void Enqueue(T elem)
        {
            if (Count >= maxcount)
            {
                System.Windows.Forms.MessageBox.Show("Кончилась память");
                Environment.Exit(0);

            }
            lock (this)
            {
                // GetSize();
                base.Enqueue(new QueueElem<T>(elem));
                Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "  Добавляем новый элемент в очередь ");
            }
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "  В очереди сейчас " + Count + " элементов");

        }



        public QueueElem<T> Get(int maxget)
        {
            Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "  Берем элемент с вершины");
        Label1:
            //    Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "MagicQueue   На вершине  " + base.Peek().count + " элементов");

            if (base.Peek().count > maxget)
            {
                QueueElem<T> result = new QueueElem<T>(base.Peek().elem) { count = maxget };
                base.Peek().count = base.Peek().count - maxget;
                Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), " Взяли с вершины " + result.count + " элементов");
                return result;
            }
            else if (base.Peek().count > 0)
            {
                QueueElem<T> result = new QueueElem<T>(base.Peek().elem) {count = base.Peek().count };
                base.Peek().count = 0;
                Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), " Взяли с вершины " + result.count + " элементов");
                return result;
            }
            else
            {
                if (Count > 1)
                {
                    T bmp = base.Dequeue().elem;
                    lock (bmp)
                    {
                        bmp.Dispose();
                    }
                    goto Label1;
                }
                else
                {
                    throw new Exception("XYU");
                }
            }

        }



        public bool CanWrite()
        {

            lock (this)
            {
                foreach (QueueElem<T> elem in this)
                {
                    if (elem.count > 0)
                        return true;
                }
                return false;
            }
        }

        public void Close()
        {
            if (t != null)
            {
                t.Dispose();
                t = null; Log.LogClass.Enqueue(MethodBase.GetCurrentMethod(), "  В очереди сейчас " + Count + " элементов");

            }
        }

        public void Dispose()
        {
            Close();
            lock (this)
            {
                foreach (QueueElem<T> elem in this)
                {
                    elem.elem.Dispose();
                }
                base.Clear();
            }

        }

    }


}
