using E = Euresys.EGrabber;

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace EGrabberWPF
{
    /// <summary>
    /// Interaction logic for EGrabberWindow.xaml
    /// </summary>
    public partial class EGrabberWindow : Window
    {
        private E.EGenTL gentl;
        private E.EGrabberDiscovery discovery;
        private E.EGrabber grabber;
        private E.Buffer lastBuffer;
        private readonly object lastBufferLock = new object();
        private Task processEventsTask;
        private CancellationTokenSource cancelProcessEventsTokenSource;
        private readonly System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
        private WriteableBitmap imageBitmapSource;
        private Int32Rect dirtRect;

        public EGrabberWindow()
        {
            InitializeComponent();
            try
            {
                gentl = new E.EGenTL();
                discovery = new E.EGrabberDiscovery(gentl);
                discovery.Discover();
                if (discovery.CameraCount == 0)
                {
                    throw new Exception("No cameras discovered");
                }
                grabber = new E.EGrabber(discovery.Cameras[0]);
                grabber.ReallocBuffers(10);
                InitBitmapSource();
                dispatcherTimer.Tick += new EventHandler(UpdateImageBitmap);
                dispatcherTimer.Interval = TimeSpan.FromMilliseconds(20);
                grabber.RegisterEventCallback<E.NewBufferData>(OnNewBuffer);
            }
            catch (Exception)
            {
                DisposeUnmanagedResources();
                throw;
            }
        }

        public void DisposeUnmanagedResources()
        {
            if (lastBuffer != null)
            {
                lastBuffer.Dispose();
                lastBuffer = null;
            }
            if (grabber != null)
            {
                grabber.Dispose();
                grabber = null;
            }
            if (discovery != null)
            {
                discovery.Dispose();
                discovery = null;
            }
            if (gentl != null)
            {
                gentl.Dispose();
                gentl = null;
            }
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = true;
            Hide();
            StopStream();
        }

        private void InitBitmapSource()
        {
            int width = (int)grabber.Width;
            int height = (int)grabber.Height;
            dirtRect = new Int32Rect(0, 0, width, height);
            imageBitmapSource = new WriteableBitmap(width, height, 96, 96, System.Windows.Media.PixelFormats.Bgr24, null);
            EGrabberImage.Source = imageBitmapSource;
        }

        private void UpdateImageBitmap(object sender, EventArgs e)
        {
            E.Buffer buffer = null;
            lock (lastBufferLock)
            {
                if (lastBuffer != null)
                {
                    buffer = lastBuffer;
                    lastBuffer = null;
                }
            }
            if (buffer != null)
            {
                try
                {
                    statusPixelFormat.Text = gentl.ImageGetPixelFormat(buffer.GetInfo<ulong>(E.BUFFER_INFO_CMD.BUFFER_INFO_PIXELFORMAT));
                }
                catch
                { }
                try
                {
                    statusResolution.Text = string.Format("{0}x{1}",
                        buffer.GetInfo<ulong>(E.BUFFER_INFO_CMD.BUFFER_INFO_WIDTH),
                        buffer.GetInfo<ulong>(E.BUFFER_INFO_CMD.BUFFER_INFO_DELIVERED_IMAGEHEIGHT));
                }
                catch
                { }
                try
                {
                    imageBitmapSource.Lock();
                    buffer.ConvertTo("BGR8", imageBitmapSource.BackBuffer, (ulong)(imageBitmapSource.BackBufferStride * imageBitmapSource.PixelHeight));
                    imageBitmapSource.AddDirtyRect(dirtRect);
                }
                finally
                {
                    imageBitmapSource.Unlock();
                    buffer.Push();
                    buffer.Dispose();
                }
            }
            try
            {
                statusFrameRate.Text = grabber.Stream.Get<double>("StatisticsFrameRate").ToString("f1");
            }
            catch
            { }
        }

        public void OnNewBuffer(E.EGrabber g, E.NewBufferData data)
        {
            lock (lastBufferLock)
            {
                if (lastBuffer != null)
                {
                    // push back the last buffer
                    lastBuffer.Push();
                    lastBuffer.Dispose();
                    lastBuffer = null;
                }
                // keep the last received buffer for display (see UpdateImageBitmap)
                lastBuffer = new E.Buffer(g, data);
            }
        }

        public void StartStream()
        {
            if (processEventsTask == null && grabber != null)
            {
                grabber.Start();
                cancelProcessEventsTokenSource = new CancellationTokenSource();
                processEventsTask = grabber.ProcessEventsAsync(E.EventType.NewBufferData,
                    cancelProcessEventsTokenSource.Token);
                dispatcherTimer.Start();
            }
        }

        public void StopStream()
        {
            if (processEventsTask != null && grabber != null)
            {
                dispatcherTimer.Stop();
                grabber.Stop();
                cancelProcessEventsTokenSource.Cancel();
                processEventsTask.Wait();
                processEventsTask = null;
                cancelProcessEventsTokenSource.Dispose();
                cancelProcessEventsTokenSource = null;
                if (lastBuffer != null)
                {
                    lastBuffer.Push();
                    lastBuffer.Dispose();
                    lastBuffer = null;
                }
                grabber.FlushEvent(E.EventType.All);
                statusFrameRate.Text = "-";
            }
        }

        public string GetGrabberInfo()
        {
            return grabber == null ? "<no grabber>" : grabber.ToString();
        }
    }
}
