using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Shapes;
using System.Threading;
using Microsoft.Kinect;

namespace TeacherAssitant
{
    /// <summary>
    /// Interaction logic for Cover.xaml
    /// </summary>
    partial class Cover : Window
    {


        /// <summary>
        /// The Kinect.
        /// </summary>
        private KinectSensor sensor;

        /// <summary>
        /// Current skeleton data for all the players.
        /// </summary>
        private Skeleton[] skeletonData;

        /// <summary>
        /// Teachers.
        /// </summary>
        private readonly Dictionary<int, Teacher> teachers = new Dictionary<int, Teacher>();

        private Teacher activeTeacher;


        #region Images

        private Image actualCursor;

        private Image handCursor;

        private Image[] selectionAni;

        private TextBlock txtMsg;

        #endregion Images

        private PowerPointPresentation selection;
        private PptControls controlSelection;
        private int counter;
        private int secCounter;
        private Image actualSelectionAni;

        public Rectangle pptPreviousSlider;
        public Rectangle pptSliderLeftZone;
        public Rectangle pptSliderRightZone;
        public Rectangle pptNextSlider;
        public Image prevArrow;
        public Image nextArrow;
        public Image pptCerrar;
        public Line sliderPosition;

        private double x;
        private double y;

        private MainWindow principal;

        public Cover(MainWindow principal)
        {
            this.principal = principal;
            InitializeComponent();
            Init();
        }

        private void Init()
        {
            initializeImages();
            txtMsg = new TextBlock { Text = "Status", Foreground = Brushes.White };
            Canvas.SetLeft(txtMsg, 50);
            Canvas.SetTop(txtMsg, 10);
            mainCanvas.Children.Add(txtMsg);
            Canvas.SetZIndex(txtMsg,98);

            //Sets the callback to identify a new Kinect sensor.
            kinectSensorChooser1.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser1_KinectSensorChanged);
        }

        private void initializeImages()
        {
            handCursor = new Image();
            handCursor.Source = new BitmapImage(new Uri("data/img/cursor1.png", UriKind.RelativeOrAbsolute));

            selectionAni = new Image[10];
            for (int i = 0; i < 10; i++)
            {
                selectionAni[i] = new Image();
                selectionAni[i].Source = new BitmapImage(new Uri("data/img/selctionAni" + (i + 1) + ".png", UriKind.RelativeOrAbsolute));
            }

            nextArrow = new Image();
            nextArrow.Source = new BitmapImage(new Uri("data/img/nextarrow.png", UriKind.RelativeOrAbsolute));
            prevArrow = new Image();
            prevArrow.Source = new BitmapImage(new Uri("data/img/prevarrow.png", UriKind.RelativeOrAbsolute));
            pptCerrar = new Image();
            pptCerrar.Source = new BitmapImage(new Uri("data/img/cerrar.png", UriKind.RelativeOrAbsolute));
        }
        
        /// <summary>
        /// This callback is invoked each time a new Kinect sensor is identified.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void kinectSensorChooser1_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            //Stop last sensor.
            KinectSensor oldSensor = (KinectSensor)e.OldValue;
            StopKinect(oldSensor);

            //Enable depth, skeleton and color stream.
            KinectSensor newSensor = (KinectSensor)e.NewValue;
            newSensor.DepthStream.Enable();
            newSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
            newSensor.SkeletonStream.Enable(new TransformSmoothParameters()
            {
                Smoothing = 0.0f,
                Correction = 0.0f,
                Prediction = 1.0f,
                JitterRadius = 1.0f,
                MaxDeviationRadius = 0.7f
            });

            //Add callbacks for depth frame and skeleton frame.
            newSensor.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(skeletonFrameReady);

            //Add the Kinect to the skeleton viewer.
            kinectSkeletonViewer1.Kinect = newSensor;
            kinectColorViewer1.Kinect = newSensor;

            try
            {
                //Start the stream nad reset the elevation angle.
                newSensor.Start();
                updateStatus("started");
            }
            catch (System.IO.IOException)
            {
                kinectSensorChooser1.AppConflictOccurred();
                return;
            }
        }


        public void updateCursorPosition(double[] position)
        {
            if (position != null)
            {
                if (actualCursor == null)
                {
                    actualCursor = handCursor;
                    mainCanvas.Children.Add(actualCursor); 
                    Canvas.SetZIndex(actualCursor,99);
                }

                x = position[0];
                y = position[1];
                Canvas.SetLeft(actualCursor, position[0] - (actualCursor.ActualWidth / 2));
                Canvas.SetTop(actualCursor, position[1] - (actualCursor.ActualHeight / 2));

                if (actualSelectionAni != null)
                {
                    Canvas.SetLeft(actualSelectionAni, x - (actualSelectionAni.ActualWidth / 2));
                    Canvas.SetTop(actualSelectionAni, y - (actualSelectionAni.ActualHeight / 2));
                }
            }
            else
            {
                if (actualCursor != null)
                {
                    mainCanvas.Children.Remove(actualCursor);
                    actualCursor = null;
                }
            }
        }

        public void updateStatus(string txt)
        {
            txtMsg.Text = txt;
        }

        public void advanceSelection(PowerPointPresentation ppt, Func<PowerPointPresentation, int> retreive)
        {
            if (ppt == selection)
            {
                secCounter++;
                if (secCounter == 10)
                {
                    secCounter = 0;

                    counter++;
                    if (counter == 11)
                    {
                        retreive(selection);
                        selection = null;
                        counter = 0;
                    }
                    else
                    {
                        if (actualSelectionAni != null)
                        {
                            mainCanvas.Children.Remove(actualSelectionAni);
                        }

                        actualSelectionAni = selectionAni[counter - 1];
                        Canvas.SetLeft(actualSelectionAni, x - (actualSelectionAni.ActualWidth / 2));
                        Canvas.SetTop(actualSelectionAni, y - (actualSelectionAni.ActualHeight / 2));
                        mainCanvas.Children.Add(actualSelectionAni);
                        Canvas.SetZIndex(actualSelectionAni,99);
                    }
                }
            }
            else
            {
                selection = ppt;
                counter = 1;
                secCounter = 0;

                if (actualSelectionAni != null)
                {
                    mainCanvas.Children.Remove(actualSelectionAni);
                }

                actualSelectionAni = selectionAni[counter - 1];
                Canvas.SetLeft(actualSelectionAni, x - (actualSelectionAni.ActualWidth / 2));
                Canvas.SetTop(actualSelectionAni, y - (actualSelectionAni.ActualHeight / 2));
                mainCanvas.Children.Add(actualSelectionAni); 
                Canvas.SetZIndex(actualSelectionAni,99);
            }
        }

        public void noSelection()
        {
            selection = null;
            counter = 1;
            secCounter = 0;

            if (actualSelectionAni != null)
            {
                mainCanvas.Children.Remove(actualSelectionAni);
            }
        }

        public void loadPresentationControls()
        {
            pptPreviousSlider = new Rectangle
            {
                Width = mainCanvas.ActualWidth - 400,
                Height = 300,
                Fill = new LinearGradientBrush(Color.FromArgb(200, 0, 250, 250), Color.FromArgb(50, 0, 250, 250), 0)
            };
            pptSliderRightZone = new Rectangle
            {
                Width = 100,
                Height = 300,
                Stroke = new SolidColorBrush(Color.FromArgb(200, 0, 250, 250)),
                StrokeThickness = 2
            };
            pptNextSlider = new Rectangle
            {
                Width = mainCanvas.ActualWidth - 400,
                Height = 300,
                Fill = new LinearGradientBrush(Color.FromArgb(50, 0, 250, 250), Color.FromArgb(200, 0, 250, 250), 0)
            };
            pptSliderLeftZone = new Rectangle
            {
                Width = 100,
                Height = 300,
                Stroke = new SolidColorBrush(Color.FromArgb(200, 0, 250, 250)),
                StrokeThickness = 2
            };

            pptPreviousSlider.Opacity = 0;
            mainCanvas.Children.Add(pptPreviousSlider);
            Canvas.SetLeft(pptPreviousSlider, 200);
            Canvas.SetTop(pptPreviousSlider, mainCanvas.ActualHeight - 350);
            Canvas.SetZIndex(pptPreviousSlider,1);

            pptSliderRightZone.Opacity = 0;
            mainCanvas.Children.Add(pptSliderRightZone);
            Canvas.SetLeft(pptSliderRightZone, mainCanvas.ActualWidth - 300);
            Canvas.SetTop(pptSliderRightZone, mainCanvas.ActualHeight - 350);
            Canvas.SetZIndex(pptSliderRightZone,2);

            prevArrow.Opacity = 0;
            mainCanvas.Children.Add(prevArrow);
            Canvas.SetLeft(prevArrow, mainCanvas.ActualWidth / 2 - 80);
            Canvas.SetTop(prevArrow, mainCanvas.ActualHeight - 300 - 20);
            Canvas.SetZIndex(prevArrow,2);

            pptNextSlider.Opacity = 0;
            mainCanvas.Children.Add(pptNextSlider);
            Canvas.SetLeft(pptNextSlider, 200);
            Canvas.SetTop(pptNextSlider, mainCanvas.ActualHeight - 350);
            Canvas.SetZIndex(pptNextSlider,1);

            pptSliderLeftZone.Opacity = 0;
            mainCanvas.Children.Add(pptSliderLeftZone);
            Canvas.SetLeft(pptSliderLeftZone, 200);
            Canvas.SetTop(pptSliderLeftZone, mainCanvas.ActualHeight - 350);
            Canvas.SetZIndex(pptSliderLeftZone,2);

            nextArrow.Opacity = 0;
            mainCanvas.Children.Add(nextArrow);
            Canvas.SetLeft(nextArrow, mainCanvas.ActualWidth / 2 - 80);
            Canvas.SetTop(nextArrow, mainCanvas.ActualHeight - 300 - 20);
            Canvas.SetZIndex(nextArrow,2);

            //pptCerrar.Opacity = 0;
            mainCanvas.Children.Add(pptCerrar);
            Canvas.SetLeft(pptCerrar, 800);
            Canvas.SetTop(pptCerrar, 20);
            Canvas.SetZIndex(pptCerrar,1);
        }

        public void unloadPresentationControls()
        {
            mainCanvas.Children.Remove(pptPreviousSlider);
            mainCanvas.Children.Remove(pptSliderRightZone);
            mainCanvas.Children.Remove(prevArrow);
            mainCanvas.Children.Remove(pptNextSlider);
            mainCanvas.Children.Remove(pptSliderLeftZone);
            mainCanvas.Children.Remove(nextArrow);
            mainCanvas.Children.Remove(pptCerrar);
        }

        public void notSelected(PptControls control)
        {
            if (control == PptControls.Cerrar && pptCerrar.Opacity > 0)
            {
                pptCerrar.Opacity -= 0.01;
            }
            else if (control == PptControls.NextSlider)
            {
                pptNextSlider.Opacity = 0;
                nextArrow.Opacity = 0;
                pptSliderLeftZone.Opacity = 0;
            }
            else if (control == PptControls.PrevSlider)
            {
                pptPreviousSlider.Opacity = 0;
                prevArrow.Opacity = 0;
                pptSliderRightZone.Opacity = 0;
            }
        }

        public bool isIn(double x, double y, PptControls control)
        {
            if (control == PptControls.Cerrar)
            {
                return x >= Canvas.GetLeft(pptCerrar) && x <= Canvas.GetLeft(pptCerrar) + pptCerrar.ActualWidth && y >= Canvas.GetTop(pptCerrar) && y <= Canvas.GetTop(pptCerrar) + pptCerrar.ActualHeight;
            }
            else if (control == PptControls.LeftSliderZone)
            {
                return x >= Canvas.GetLeft(pptSliderLeftZone) && x <= Canvas.GetLeft(pptSliderLeftZone) + pptSliderLeftZone.ActualWidth && y >= Canvas.GetTop(pptSliderLeftZone) && y <= Canvas.GetTop(pptSliderLeftZone) + pptSliderLeftZone.ActualHeight;
            }
            else if (control == PptControls.RightSliderZone)
            {
                return x >= Canvas.GetLeft(pptSliderRightZone) && x <= Canvas.GetLeft(pptSliderRightZone) + pptSliderRightZone.ActualWidth && y >= Canvas.GetTop(pptSliderRightZone) && y <= Canvas.GetTop(pptSliderRightZone) + pptSliderRightZone.ActualHeight;
            }
            else if (control == PptControls.NextSlider)
            {
                return x >= Canvas.GetLeft(pptNextSlider) && x <= Canvas.GetLeft(pptNextSlider) + pptNextSlider.ActualWidth && y >= Canvas.GetTop(pptNextSlider) && y <= Canvas.GetTop(pptNextSlider) + pptNextSlider.ActualHeight;
            }
            else if (control == PptControls.PrevSlider)
            {
                return x >= Canvas.GetLeft(pptPreviousSlider) && x <= Canvas.GetLeft(pptPreviousSlider) + pptPreviousSlider.ActualWidth && y >= Canvas.GetTop(pptPreviousSlider) && y <= Canvas.GetTop(pptPreviousSlider) + pptPreviousSlider.ActualHeight;
            }

            return false;
        }

        public bool isIn2(double x, double y, PptControls control)
        {
            if (control == PptControls.LeftSliderZone)
            {
                return x <= Canvas.GetLeft(pptSliderLeftZone) + pptSliderLeftZone.ActualWidth && y >= Canvas.GetTop(pptSliderLeftZone) && y <= Canvas.GetTop(pptSliderLeftZone) + pptSliderLeftZone.ActualHeight;
            }
            else if (control == PptControls.RightSliderZone)
            {
                return x >= Canvas.GetLeft(pptSliderRightZone) && y >= Canvas.GetTop(pptSliderRightZone) && y <= Canvas.GetTop(pptSliderRightZone) + pptSliderRightZone.ActualHeight;
            }

            return false;
        }

        public void advanceSelection(PptControls control, Func<int> retreive)
        {
            if (control == PptControls.Cerrar && pptCerrar.Opacity < 1)
            {
                pptCerrar.Opacity += 0.1;
            }

            if (control == controlSelection)
            {
                secCounter++;
                if (secCounter == 10)
                {
                    secCounter = 0;

                    counter++;
                    if (counter == 11)
                    {
                        retreive();
                        selection = null;
                        counter = 0;
                    }
                    else
                    {
                        if (actualSelectionAni != null)
                        {
                            mainCanvas.Children.Remove(actualSelectionAni);
                        }

                        actualSelectionAni = selectionAni[counter - 1];
                        Canvas.SetLeft(actualSelectionAni, x - (actualSelectionAni.ActualWidth / 2));
                        Canvas.SetTop(actualSelectionAni, y - (actualSelectionAni.ActualHeight / 2));
                        mainCanvas.Children.Add(actualSelectionAni);
                        Canvas.SetZIndex(actualSelectionAni,99);
                    }
                }
            }
            else
            {
                controlSelection = control;
                counter = 1;
                secCounter = 0;

                if (actualSelectionAni != null)
                {
                    mainCanvas.Children.Remove(actualSelectionAni);
                }

                actualSelectionAni = selectionAni[counter - 1];
                Canvas.SetLeft(actualSelectionAni, x - (actualSelectionAni.ActualWidth / 2));
                Canvas.SetTop(actualSelectionAni, y - (actualSelectionAni.ActualHeight / 2));
                mainCanvas.Children.Add(actualSelectionAni);
                Canvas.SetZIndex(actualSelectionAni,99);
            }
        }

        public void showNextSlider()
        {
            pptNextSlider.Opacity = 1;
            nextArrow.Opacity = 1;
            pptSliderLeftZone.Opacity = 1;
        }

        public void showPrevSlider()
        {
            pptPreviousSlider.Opacity = 1;
            prevArrow.Opacity = 1;
            pptSliderRightZone.Opacity = 1;
        }

        public void showSlider(double x)
        {
            if (sliderPosition != null)
            {
                mainCanvas.Children.Remove(sliderPosition);
                sliderPosition = null;
            }
            sliderPosition = new Line { X1 = x, X2 = x, Y1 = mainCanvas.ActualHeight - 350, Y2 = mainCanvas.ActualHeight - 50, Stroke = Brushes.LightGreen, StrokeThickness = 8 };
            mainCanvas.Children.Add(sliderPosition);
            Canvas.SetZIndex(sliderPosition, 98);
        }

        public void hideSlider()
        {
            if (sliderPosition != null)
            {
                mainCanvas.Children.Remove(sliderPosition);
                sliderPosition = null;
            }
        }

        public enum PptControls
        {
            NextSlider,
            PrevSlider,
            LeftSliderZone,
            RightSliderZone,
            Cerrar
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            kinectSkeletonViewer1.Kinect = null;
            kinectColorViewer1.Kinect = null;
            StopKinect(kinectSensorChooser1.Kinect);
            principal.Close();
        }

        /// <summary>
        /// Stops the Kinect services.
        /// </summary>
        /// <param name="sensor"></param>
        void StopKinect(KinectSensor sensor)
        {
            if (sensor != null)
            {
                sensor.Stop();
                //sensor.AudioSource.Stop();
                //this.mySpeechRecognizer.Stop();
                //this.mySpeechRecognizer.SaidSomething -= this.RecognizerSaidSomething;
                //this.mySpeechRecognizer.Dispose();
                //this.mySpeechRecognizer = null;
            }
        }


        /// <summary>
        /// Callback called every skeleton update.
        /// </summary>
        /// <param name="sender">object that sent the event</param>
        /// <param name="e">Skeletons information</param>
        public void skeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                if (skeletonFrame != null)
                {
                    int skeletonSlot = 0;

                    if ((this.skeletonData == null) || (this.skeletonData.Length != skeletonFrame.SkeletonArrayLength))
                    {
                        this.skeletonData = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    }

                    skeletonFrame.CopySkeletonDataTo(this.skeletonData);

                    foreach (Skeleton skeleton in this.skeletonData)
                    {
                        if (SkeletonTrackingState.Tracked == skeleton.TrackingState)
                        {
                            //Identify if it is a new player.
                            Teacher teacher;
                            if (this.teachers.ContainsKey(skeletonSlot))
                            {
                                teacher = this.teachers[skeletonSlot];
                            }
                            else
                            {
                                teacher = new Teacher(skeletonSlot);
                                this.teachers.Add(skeletonSlot, teacher);
                            }
                            activeTeacher = teacher;

                            // Update player's bone and joint positions
                            if (skeleton.Joints.Count > 0)
                            {
                                teacher.LastUpdated = DateTime.Now;
                                teacher.IsAlive = true;

                                double handLeftZ = skeleton.Joints[JointType.HandLeft].Position.Z;
                                double handRightZ = skeleton.Joints[JointType.HandRight].Position.Z;
                                
                                JointType hand;
                                double zHand;
                                if (handLeftZ < handRightZ)
                                {
                                    hand = JointType.HandLeft;
                                    zHand = handLeftZ;
                                }
                                else
                                {
                                    hand = JointType.HandRight;
                                    zHand = handRightZ;
                                }

                                double shoulderRight = skeleton.Joints[JointType.ShoulderRight].Position.Z;
                                double shoulderLeft = skeleton.Joints[JointType.ShoulderLeft].Position.Z;

                                double minDist = 0.02;
                                if (skeleton.Joints[hand].TrackingState == JointTrackingState.Tracked && zHand < shoulderLeft - minDist && zHand < shoulderRight - minDist)
                                {
                                    double shoulderCenterX = skeleton.Joints[JointType.ShoulderCenter].Position.X;
                                    double shoulderCenterY = skeleton.Joints[JointType.ShoulderCenter].Position.Y;

                                    double shoulderLeftX = skeleton.Joints[JointType.ShoulderLeft].Position.X;
                                    double shoulderLeftY = skeleton.Joints[JointType.ShoulderLeft].Position.Y;
                                    double shoulderRightX = skeleton.Joints[JointType.ShoulderRight].Position.X;
                                    double shoulderRightY = skeleton.Joints[JointType.ShoulderRight].Position.Y;

                                    teacher.hasActiveHand = true;
                                    double scaleX = 1000;
                                    double scaleY = 1000;
                                    
                                    double sX = shoulderLeftX - shoulderRightX;
                                    double sY = shoulderLeftY - shoulderRightY;
                                    double sZ = shoulderLeft - shoulderRight;
                                    double bigScale = Math.Sqrt(sX*sX+sY*sY);
                                    
                                    double distX = skeleton.Joints[hand].Position.X-shoulderCenterX;
                                    double distY = shoulderCenterY - skeleton.Joints[hand].Position.Y;

                                    double alpha = 0;
                                    if(hand == JointType.HandLeft)
                                        teacher.activeHandX = teacher.activeHandX * alpha + ((2.5*bigScale + distX) * scaleX) * (1 - alpha);
                                    else
                                        teacher.activeHandX = teacher.activeHandX * alpha + ((2*bigScale + distX) * scaleX) * (1 - alpha);
                                    teacher.activeHandY = teacher.activeHandY * alpha + ((bigScale + distY) * scaleY) * (1 - alpha);
                                }
                                else
                                    teacher.hasActiveHand = false;

                            }

                        }

                        skeletonSlot++;
                    }
                }
            }
        }

        public double[] getCursorPosition()
        {
            if (activeTeacher != null && activeTeacher.hasActiveHand)
            {
                double[] position = { activeTeacher.activeHandX, activeTeacher.activeHandY };
                return position;
            }
            else
                return null;
        }

    }

}
