// Get xyz physical coordinates
        private BallCoords XYZLocation(DepthFrame depthFrame, int xavg, int yavg)
        {
            BallCoords ballLocn = new BallCoords();

            if (depthFrame == null)
            {
                return(ballLocn);
            }

            using (KinectBuffer depthFrameData = depthFrame.LockImageBuffer())
            {
                CameraSpacePoint[] camSpacePoints = new CameraSpacePoint[1920 * 1080];
                this.coordinateMapper.MapColorFrameToCameraSpaceUsingIntPtr(depthFrameData.UnderlyingBuffer, depthFrameData.Size, camSpacePoints);
                List <float> xvals  = new List <float>();
                List <float> yvals  = new List <float>();
                List <float> zvals  = new List <float>();
                int          Vcount = 0;

                // Find ball in camera space
                for (int i = -40; i < 40; i++)
                {
                    for (int j = -40; j < 40; j++)
                    {
                        if (yavg + i > tableLevel)
                        {
                            int tempIndex = (yavg + i) * 1920 + xavg + j;
                            if (camSpacePoints[tempIndex].Z > GlobalClass.minZ && camSpacePoints[tempIndex].Z < 3.6)
                            {
                                xvals.Add(camSpacePoints[tempIndex].X);
                                yvals.Add(camSpacePoints[tempIndex].Y);
                                zvals.Add(camSpacePoints[tempIndex].Z);
                                Vcount++;
                            }
                        }
                    }
                }
                if (Vcount > 0)
                {
                    ballLocn.X    = FindMedian(xvals);
                    ballLocn.Y    = FindMedian(yvals);
                    ballLocn.Z    = FindMedian(zvals);
                    ballLocn.Time = DateTime.Now;
                }
                else
                {
                    ballLocn.X = 0;
                    ballLocn.Y = 0;
                    ballLocn.Z = 0;
                }
                return(ballLocn);
            }
        }
        // Calculate ball speed in m/s
        private double BallSpeed(BallCoords currLocn, BallCoords prevLocn)
        {
            double deltat = currLocn.Time.Subtract(prevLocn.Time).TotalSeconds;

            if (currLocn.X != 0 && currLocn.Y != 0 && prevLocn.X != 0 && prevLocn.Y != 0 && deltat > 0.025 && deltat < 0.07)
            {
                double deltax   = currLocn.X - prevLocn.X;
                double deltay   = currLocn.Y - prevLocn.Y;
                double distance = Math.Sqrt(deltax * deltax + deltay * deltay);
                SpeedData[SpeedIndex] = currLocn.X + "  " + prevLocn.X + "  " + currLocn.Y + "  " + prevLocn.Y + "  " + distance + "  " + deltat;
                SpeedIndex++;
                return(distance / deltat);
            }
            else
            {
                return(0);
            }
        }
        // Color frame analysis With ball action analysis
        private void Frame_Arrived(object sender, MultiSourceFrameArrivedEventArgs e)
        {
            bool changeDir = false;


            if (scoreDelay != DateTime.MinValue)
            {
                if ((float)DateTime.Now.Subtract(this.scoreDelay).TotalSeconds < 2)
                {
                    pause = true;
                }
                else
                {
                    scoreDelay = DateTime.MinValue;
                    pause      = false;
                }
            }

            if (!gameOver && !pause)
            {
                MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();

                if (inVolley)
                {
                    // If too much time passes without bounce/return, someone missed
                    if (this.hitTime != DateTime.MinValue)
                    {
                        if ((float)DateTime.Now.Subtract(this.hitTime).TotalSeconds > 1.5)
                        {
                            if (this.Direction == "Left" && bounce1)
                            {
                                Score("P2", "Time Limit - no return");
                            }
                            else if (this.Direction == "Left")
                            {
                                Score("P1", "Time Limit - no bounce");
                            }
                            else if (this.Direction == "Right" && bounce1)
                            {
                                Score("P1", "Time Limit - no return");
                            }
                            else
                            {
                                Score("P2", "Time Limit - no bounce");
                            }
                        }
                    }

                    // Get Ball xy coordinates
                    DataPoint BallLocation = FindBall(multiSourceFrame);
                    int       xavg         = (int)BallLocation.X;
                    int       yavg         = (int)BallLocation.Y;

                    // Check if location seems reasonable, set xavg to 1 (default no ball found value) if not
                    if (AllData.Count > 0)
                    {
                        if (!ReasonableLocation(BallLocation))
                        {
                            xavg = 1;
                        }
                    }

                    // If good data point, analyze it
                    if (xavg > 1)
                    {
                        // Get ball xyz coordinates
                        using (DepthFrame depthFrame = multiSourceFrame.DepthFrameReference.AcquireFrame())
                        {
                            CurrentXYZ = XYZLocation(depthFrame, xavg, yavg);
                        }

                        // Off (or rather under) table
                        if (yavg < tableLevel - 100)
                        {
                            if (bounce1)
                            {
                                if (this.Direction == "Left")
                                {
                                    Score("P2", "Below Table - no return");
                                }
                                else
                                {
                                    Score("P1", "Below Table - no return");
                                }
                            }
                            else if (served)
                            {
                                if (this.Direction == "Left")
                                {
                                    Score("P1", "Below Table - no bounce");
                                }
                                else
                                {
                                    Score("P2", "Below Table - no bounce");
                                }
                            }
                            else
                            {
                                ServeIsBad();
                            }
                        }

                        // Determine direction and do game processing
                        float xdelta = 0;
                        float ydelta = 0;
                        if (AllData.Count > 0)
                        {
                            xdelta = AllData[AllData.Count - 1].X - xavg;
                            ydelta = AllData[AllData.Count - 1].Y - yavg;
                        }

                        if (served)
                        {
                            // Horizontal direction determination and direction change detection
                            if (xdelta > 10)
                            {
                                if (this.Direction == "Right")
                                {
                                    ChangeDirection(ydelta);
                                    changeDir = true;
                                }
                                this.Direction = "Left";
                                PossibleNet    = false;
                            }
                            else if (xdelta < -10)
                            {
                                if (this.Direction == "Left")
                                {
                                    ChangeDirection(ydelta);
                                    changeDir = true;
                                }
                                this.Direction = "Right";
                                PossibleNet    = false;
                            }
                            // Check for possible hitting net - two frames at net means a score
                            else if (xavg - netLocation < 20 && xavg - netLocation > 0 && Direction == "Left")
                            {
                                if (PossibleNet)
                                {
                                    Score("P1", "Hit Net");
                                }
                                else
                                {
                                    PossibleNet = true;
                                }
                            }
                            else if (xavg - netLocation > -20 && xavg - netLocation < 0 && Direction == "Right")
                            {
                                if (PossibleNet)
                                {
                                    Score("P2", "Hit Net");
                                }
                                else
                                {
                                    PossibleNet = true;
                                }
                            }

                            // Vertical direction and bounce detection
                            if (ydelta > 5)
                            {
                                this.VertDir   = "Down";
                                PossibleBounce = false;
                            }
                            else if (ydelta < -5)
                            {
                                if (this.VertDir == "Down" && !changeDir && yavg - tableLevel < 100)   // Log possible bounce
                                {
                                    PossibleBounce  = true;
                                    this.hitTime    = DateTime.Now;
                                    this.tempBounce = new DataPoint(xavg, yavg, 0, 0);
                                }
                                else if (PossibleBounce)  // if no direction change one frame later, possible bounce is a bounce
                                {
                                    if (!changeDir)
                                    {
                                        // Handle bounce processing
                                        if (PreviousXYZ.X != 0 && PreviousXYZ.Y != 0 && PreviousXYZ.Z != 0 && PreviousXYZ.Z > GlobalClass.minZ && this.serveBounce)
                                        {
                                            HitLocation bounceXYZ = new HitLocation();
                                            bounceXYZ.X      = PreviousXYZ.X;
                                            bounceXYZ.Y      = PreviousXYZ.Y;
                                            bounceXYZ.Z      = PreviousXYZ.Z;
                                            bounceXYZ.Volley = VolleyNumber;
                                            this.Bounces.Add(bounceXYZ);
                                        }
                                        Bounce(new DataPoint(tempBounce.X, tempBounce.Y, 0, (float)(DateTime.Now.Subtract(this.timeStarted).TotalSeconds)));
                                    }
                                    PossibleBounce = false;
                                }
                                this.VertDir = "Up";
                            }
                            else if (PossibleBounce)
                            {
                                PossibleBounce = false;
                            }
                            else if (ydelta <= 0)
                            {
                                if (this.VertDir == "Down" && !changeDir && yavg - tableLevel < 100)   // Log possible bounce
                                {
                                    PossibleBounce  = true;
                                    this.hitTime    = DateTime.Now;
                                    this.tempBounce = new DataPoint(xavg, yavg, 0, 0);
                                }
                            }
                        }
                        else if (inVolley)    // if not served yet, check for serve hit
                        {
                            // check for low, shallow bounce serve
                            int xStartDelta = 0;
                            if (Server == "P1")
                            {
                                xStartDelta = xavg - (int)startLocation.X;
                            }
                            else
                            {
                                xStartDelta = (int)startLocation.X - xavg;
                            }

                            if (xStartDelta > 50)
                            {
                                served          = true;
                                VolleyStartTime = DateTime.Now;
                                if (ydelta > 10)
                                {
                                    VertDir = "Down";
                                }
                                else if (yavg - tableLevel < 50)
                                {
                                    VertDir     = "Up";
                                    serveBounce = true;
                                }

                                if (xdelta > 0)
                                {
                                    this.Direction = "Left";
                                }
                                else
                                {
                                    this.Direction = "Right";
                                }
                            }

                            // Serve defined as moving in x and negative y (slower serve, easy bounce detection)
                            else if ((xdelta > 10 || xdelta < 10) && ydelta > 10 && xStartDelta > 50)
                            {
                                this.served     = true;
                                VolleyStartTime = DateTime.Now;
                                this.VertDir    = "Down";
                                if (xdelta > 0)
                                {
                                    this.Direction = "Left";
                                }
                                else
                                {
                                    this.Direction = "Right";
                                }
                            }
                        }
                        // Add current location to points list
                        this.AllData.Add(new DataPoint(xavg, yavg, 0, (float)(DateTime.Now.Subtract(this.timeStarted).TotalSeconds)));
                        double currentSpeed = 0;

                        // Check ball speed
                        if (PreviousXYZ != null)
                        {
                            currentSpeed = BallSpeed(CurrentXYZ, PreviousXYZ);
                        }
                        if (currentSpeed > maxSpeed)
                        {
                            maxSpeed = currentSpeed;
                        }
                        PreviousXYZ = CurrentXYZ;
                    }
                }
                else   // Check for ball in start position to start volley
                {
                    // Get Ball xy coordinates
                    DataPoint BallLocation = FindBall(multiSourceFrame);
                    int       xavg         = (int)BallLocation.X;
                    int       yavg         = (int)BallLocation.Y;

                    CheckStartPosition(xavg, yavg);
                }
            }
        }