using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using Iisu;

namespace bbiwarg.Input.InputProviding
{
    /// <summary>
    /// signature for the event that the device started
    /// </summary>
    /// <param name="sender">sender of the event</param>
    /// <param name="e">arguments of the event</param>
    public delegate void DeviceStartedEventHandler(object sender, EventArgs e);

    /// <summary>
    /// signature for the event that a new frame is available
    /// </summary>
    /// <param name="sender">sender of the event</param>
    /// <param name="e">arguments of the event</param>
    public delegate void NewFrameEventHandler(object sender, NewFrameEventArgs e);

    /// <summary>
    /// Encapsulates the arguments of the event that a new frame is available.
    /// </summary>
    public class NewFrameEventArgs : EventArgs
    {
        /// <summary>
        /// the id of the frame
        /// </summary>
        public int FrameID { get; private set; }

        /// <summary>
        /// the with of all images in the frame
        /// </summary>
        public int Width { get; private set; }

        /// <summary>
        /// the height of all images in the frame
        /// </summary>
        public int Height { get; private set; }

        /// <summary>
        /// pointer to the raw depth data for the frame
        /// </summary>
        public IntPtr RawDepthData { get; private set; }

        /// <summary>
        /// pointer to the raw confidence data for the frame
        /// </summary>
        public IntPtr RawConfidenceData { get; private set; }

        /// <summary>
        /// Constructs a NewFrameEventArgs.
        /// </summary>
        /// <param name="frameID">frame id</param>
        /// <param name="width">width of all images</param>
        /// <param name="height">height of all images</param>
        /// <param name="rawDepthData">pointer to raw depth data</param>
        /// <param name="rawConfidenceData">pointer to raw confidence data</param>
        public NewFrameEventArgs(int frameID, int width, int height, IntPtr rawDepthData, IntPtr rawConfidenceData)
        {
            FrameID = frameID;
            Width = width;
            Height = height;
            RawDepthData = rawDepthData;
            RawConfidenceData = rawConfidenceData;
        }
    }

    /// <summary>
    /// InputProvider provides the raw depth and confidence data through an event.
    /// </summary>
    public class InputProvider
    {
        /// <summary>
        /// iisu handle 
        /// </summary>
        protected IHandle handle;

        /// <summary>
        /// iisu device from which the data is read
        /// </summary>
        protected IDevice device;


        /// <summary>
        /// paramter handle for the frame rate
        /// </summary>
        protected IParameterHandle<float> frameRate;

        /// <summary>
        /// parameter handle for the image width
        /// </summary>
        protected IParameterHandle<int> width;

        /// <summary>
        /// parameter handle for the image height
        /// </summary>
        protected IParameterHandle<int> height;

        /// <summary>
        /// parameter handle for the horizontal field of view
        /// </summary>
        protected IParameterHandle<float> hfov;

        /// <summary>
        /// parameter handle for the vertical field of view
        /// </summary>
        protected IParameterHandle<float> vfov;

        /// <summary>
        /// data handle for the raw depth data
        /// </summary>
        protected IDataHandle<Iisu.Data.IImageData> depthImage;

        /// <summary>
        /// data handle for the raw confidence data
        /// </summary>
        protected IDataHandle<Iisu.Data.IImageData> confidenceImage;

        /// <summary>
        /// the width of all images
        /// </summary>
        public int ImageWidth { get { return width.Value; } }

        /// <summary>
        /// the height of all images
        /// </summary>
        public int ImageHeight { get { return height.Value; } }

        /// <summary>
        /// the horizontal field of view
        /// </summary>
        public float HFOV { get { return hfov.Value; } }

        /// <summary>
        /// the vertical field of view
        /// </summary>
        public float VFOV { get { return vfov.Value; } }

        /// <summary>
        /// true iff the input source provides data
        /// </summary>
        public bool IsActive { get; private set; }

        /// <summary>
        /// the id of the current frame
        /// </summary>
        public virtual int CurrentFrameID { get { return device.FrameId; } }


        /// <summary>
        /// event that the device started
        /// </summary>
        public event DeviceStartedEventHandler DeviceStartedEvent;

        /// <summary>
        /// event that a new frame is available
        /// </summary>
        public event NewFrameEventHandler NewFrameEvent;


        /// <summary>
        /// Constructs an InputProvider.
        /// </summary>
        public InputProvider()
        {
            IsActive = false;
        }

        /// <summary>
        /// Initializes to device and data handles.
        /// </summary>
        public void initialize()
        {
            createDevice();
            registerHandles();
        }

        /// <summary>
        /// Starts the device.
        /// </summary>
        public void start()
        {
            device.Start();
            IsActive = true;

            if (DeviceStartedEvent != null)
                DeviceStartedEvent(this, new EventArgs());

            run();
        }

        /// <summary>
        /// Stops the device.
        /// </summary>
        public void stop()
        {
            IsActive = false;
            device.Stop(true);
        }

        /// <summary>
        /// Creates an iisu device which provides the data.
        /// </summary>
        protected void createDevice()
        {
            handle = Iisu.Iisu.Context.CreateHandle();
            IDeviceConfiguration conf = createDeviceConfiguration();
            device = handle.InitializeDevice(conf);
        }

        /// <summary>
        /// Returns an iisu device configuration.
        /// </summary>
        /// <returns>iisu device configuration</returns>
        protected virtual IDeviceConfiguration createDeviceConfiguration()
        {
            IDeviceConfiguration conf = handle.CreateDeviceConfiguration();
            conf.IsAsynchronous = false;
            return conf;
        }

        /// <summary>
        /// Registers all parameter and data handles.
        /// </summary>
        protected virtual void registerHandles()
        {
            width = device.RegisterParameterHandle<int>("SOURCE.CAMERA.DEPTH.Width");
            height = device.RegisterParameterHandle<int>("SOURCE.CAMERA.DEPTH.Height");
            hfov = device.RegisterParameterHandle<float>("SOURCE.CAMERA.DEPTH.HFOV");
            vfov = device.RegisterParameterHandle<float>("SOURCE.CAMERA.DEPTH.VFOV");
            frameRate = device.RegisterParameterHandle<float>("SOURCE.FrameRate");

            depthImage = device.RegisterDataHandle<Iisu.Data.IImageData>("SOURCE.CAMERA.DEPTH.Image");
            confidenceImage = device.RegisterDataHandle<Iisu.Data.IImageData>("SOURCE.CAMERA.CONFIDENCE.Image");
        }

        /// <summary>
        /// Provides the main loop for reading data from the device.
        /// </summary>
        protected virtual void run()
        {
            while (IsActive)
                nextFrame();
        }

        /// <summary>
        /// Gets the next frame from the device.
        /// </summary>
        protected virtual void nextFrame()
        {
            device.UpdateFrame(true);
            provideNewFrame();
            device.ReleaseFrame();
        }

        /// <summary>
        /// Triggers the new frame event.
        /// </summary>
        protected void provideNewFrame()
        {
            if (NewFrameEvent != null)
                NewFrameEvent(this, new NewFrameEventArgs(CurrentFrameID, ImageWidth, ImageHeight, depthImage.Value.Raw, confidenceImage.Value.Raw));
        }
    }
}