using Microsoft.Kinect;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private KinectSensor kinectSensor = null;

        // Attribut captor color
        private ColorFrameReader colorFrameReader = null;
        private WriteableBitmap colorBitmap = null;

        // Attribut captor Body
        private BodyFrameReader bodyFrameReader = null;
        private Body[] bodies = null;

        // Attribut captor Depth
        private DepthFrameReader depthFrameReader = null;
        private WriteableBitmap depthBitmap = null;
        private byte[] depthPixels;

        // Propriété publique pour le binding
        public WriteableBitmap ColorBitmap
        {
            get { return this.colorBitmap; }
        }

        public WriteableBitmap DepthBitmap
        {
            get { return this.depthBitmap; }
        }

        public MainWindow()
        {
            InitializeComponent();

            // Définir le DataContext pour le binding
            this.DataContext = this;

            // Initialiser la Kinect
            this.kinectSensor = KinectSensor.GetDefault();
            
            /* Capteur couleur
            // Ouvrir le lecteur de flux de couleur
            this.colorFrameReader = this.kinectSensor.ColorFrameSource.OpenReader();

            // Frame description pour les images de couleur
            FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);

            // Créer le bitmap pour afficher l'image
            this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);

            // Gérer l'événement FrameArrived pour le flux de couleur
            this.colorFrameReader.FrameArrived += this.Reader_ColorFrameArrived;
            */

            // Initialisation du BodyFrameReader
            this.bodyFrameReader = this.kinectSensor.BodyFrameSource.OpenReader();
            this.bodyFrameReader.FrameArrived += this.Reader_BodyFrameArrived;

            // Initialiser le tableau des corps
            this.bodies = new Body[this.kinectSensor.BodyFrameSource.BodyCount];

            // Initialisation du DepthFrameReader
            this.depthFrameReader = this.kinectSensor.DepthFrameSource.OpenReader();
            this.depthFrameReader.FrameArrived += this.Reader_DepthFrameArrived;

            FrameDescription depthFrameDescription = this.kinectSensor.DepthFrameSource.FrameDescription;

            // Initialisez depthPixels pour stocker les données de chaque pixel
            this.depthPixels = new byte[depthFrameDescription.Width * depthFrameDescription.Height];

            // Initialisez depthBitmap pour afficher les données de profondeur
            this.depthBitmap = new WriteableBitmap(depthFrameDescription.Width, depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray8, null);

            // Ouvrir la Kinect
            this.kinectSensor.Open();
        }

        private void Reader_ColorFrameArrived(object sender, ColorFrameArrivedEventArgs e)
        {
            using (ColorFrame colorFrame = e.FrameReference.AcquireFrame())
            {
                if (colorFrame != null)
                {
                    FrameDescription colorFrameDescription = colorFrame.FrameDescription;

                    using (KinectBuffer colorBuffer = colorFrame.LockRawImageBuffer())
                    {
                        this.colorBitmap.Lock();

                        // Vérifier si la taille de l'image a changé
                        if ((colorFrameDescription.Width == this.colorBitmap.PixelWidth) && (colorFrameDescription.Height == this.colorBitmap.PixelHeight))
                        {
                            colorFrame.CopyConvertedFrameDataToIntPtr(
                                this.colorBitmap.BackBuffer,
                                (uint)(colorFrameDescription.Width * colorFrameDescription.Height * 4),
                                ColorImageFormat.Bgra);

                            this.colorBitmap.AddDirtyRect(new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight));
                        }

                        this.colorBitmap.Unlock();
                    }
                }
            }
        }

        // Assurez-vous de fermer correctement le lecteur et le capteur Kinect lors de la fermeture de la fenêtre
        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.colorFrameReader != null)
            {
                this.colorFrameReader.Dispose();
                this.colorFrameReader = null;
            }

            if (this.kinectSensor != null)
            {
                this.kinectSensor.Close();
                this.kinectSensor = null;
            }
        }

        private void Reader_BodyFrameArrived(object sender, BodyFrameArrivedEventArgs e)
        {
            using (var bodyFrame = e.FrameReference.AcquireFrame())
            {
                if (bodyFrame != null)
                {
                    bodyFrame.GetAndRefreshBodyData(this.bodies);

                    skeletonCanvas.Children.Clear(); // Nettoyer le canvas avant de dessiner

                    foreach (var body in this.bodies)
                    {
                        if (body.IsTracked)
                        {
                            // Dessiner le squelette
                            DrawSkeleton(body);
                        }
                    }
                }
            }
        }

        private void DrawSkeleton(Body body)
        {
            foreach (JointType jointType in body.Joints.Keys)
            {
                Joint joint = body.Joints[jointType];
                if (joint.TrackingState == TrackingState.Tracked)
                {
                    // Convertir les coordonnées du joint en coordonnées de l'écran
                    Point point = new Point();
                    ColorSpacePoint colorPoint = this.kinectSensor.CoordinateMapper.MapCameraPointToColorSpace(joint.Position);
                    point.X = float.IsInfinity(colorPoint.X) ? 0 : colorPoint.X;
                    point.Y = float.IsInfinity(colorPoint.Y) ? 0 : colorPoint.Y;

                    // Dessiner le joint
                    DrawJoint(point);
                }
            }

            // Dessinez les os ici si nécessaire
        }

        private void DrawJoint(Point point)
        {
            Ellipse ellipse = new Ellipse
            {
                Width = 10,
                Height = 10,
                Fill = new SolidColorBrush(Colors.Red)
            };

            Canvas.SetLeft(ellipse, point.X - ellipse.Width / 2);
            Canvas.SetTop(ellipse, point.Y - ellipse.Height / 2);

            skeletonCanvas.Children.Add(ellipse);
        }

        private void Reader_DepthFrameArrived(object sender, DepthFrameArrivedEventArgs e)
        {
            using (DepthFrame depthFrame = e.FrameReference.AcquireFrame())
            {
                if (depthFrame != null)
                {
                    FrameDescription depthFrameDescription = depthFrame.FrameDescription;

                    // Créez un tableau pour stocker les données de profondeur
                    ushort[] depthData = new ushort[depthFrameDescription.LengthInPixels];
                    depthFrame.CopyFrameDataToArray(depthData);

                    // Traitez les données de profondeur
                    ProcessDepthFrameData(depthData, depthFrameDescription.LengthInPixels, depthFrame.DepthMinReliableDistance, depthFrame.DepthMaxReliableDistance);

                    // Mettez à jour le bitmap de profondeur
                    this.depthBitmap.WritePixels(
                        new Int32Rect(0, 0, depthFrameDescription.Width, depthFrameDescription.Height),
                        this.depthPixels,
                        depthFrameDescription.Width,
                        0);
                }
            }
        }

        private void ProcessDepthFrameData(ushort[] depthData, uint depthFrameDataSize, ushort minDepth, ushort maxDepth)
        {
            // Convertir les données de profondeur en niveaux de gris
            for (int i = 0; i < depthFrameDataSize; ++i)
            {
                ushort depth = depthData[i];
                this.depthPixels[i] = (byte)(depth >= minDepth && depth <= maxDepth ? (depth % 256) : 0);
            }
        }

    }
}
