// Remove a lost UserId
        public virtual int RemoveUser(ulong userId, KinectManager.UserDetectionOrder userDetectionOrder)
        {
            //int uidIndex = alUserIds.IndexOf(userId);
            int uidIndex = System.Array.IndexOf(aUserIndexIds, userId);

            // clear calibration data for this user
            if (playerCalibrationData.ContainsKey(userId))
            {
                playerCalibrationData.Remove(userId);
            }

            // clean up the outdated calibration data in the data dictionary
            List <ulong> alCalDataKeys = new List <ulong>(playerCalibrationData.Keys);

            foreach (ulong calUserID in alCalDataKeys)
            {
                KinectGestureManager.GestureData gestureData = playerCalibrationData[calUserID];

                if ((gestureData.timestamp + 60f) < Time.realtimeSinceStartup)
                {
                    playerCalibrationData.Remove(calUserID);
                }
            }

            alCalDataKeys.Clear();

            // remove user-id from the global users lists
            dictUserIdToIndex.Remove(userId);
            dictUserIdToTime.Remove(userId);
            alUserIds.Remove(userId);

            if (uidIndex >= 0)
            {
                FreeEmptyUserSlot(uidIndex, userDetectionOrder);
            }

            // if this was the primary user, update the primary user-id
            if (liPrimaryUserId == userId)
            {
                if (aUserIndexIds.Length > 0)
                {
                    liPrimaryUserId = aUserIndexIds[0];
                }
                else
                {
                    liPrimaryUserId = 0;
                }
            }

            if (alUserIds.Count == 0)
            {
                //Debug.Log("Waiting for users.");
            }

            return(uidIndex);
        }
        // releases the user slot. rearranges the remaining users.
        protected virtual void FreeEmptyUserSlot(int uidIndex, KinectManager.UserDetectionOrder userDetectionOrder)
        {
            aUserIndexIds[uidIndex] = 0;

            if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
            {
                // rearrange the remaining users
                for (int u = uidIndex; u < (aUserIndexIds.Length - 1); u++)
                {
                    aUserIndexIds[u] = aUserIndexIds[u + 1];

                    if (aUserIndexIds[u + 1] != 0)
                    {
                        Debug.Log(string.Format("Reindexing user {0} to {1}, ID: {2}.", u + 1, u, aUserIndexIds[u + 1]));
                    }
                }

                // make sure the last slot is free
                aUserIndexIds[aUserIndexIds.Length - 1] = 0;
            }

            // rearrange the remaining users
            RearrangeUserIndices(userDetectionOrder);
        }
        // Adds UserId to the list of users
        public virtual int CalibrateUser(ulong userId, int bodyIndex, ref KinectInterop.BodyData[] alTrackedBodies,
                                         KinectManager.UserDetectionOrder userDetectionOrder, GestureType playerCalibrationPose, KinectGestureManager gestureManager)
        {
            if (!alUserIds.Contains(userId))
            {
                if (CheckForCalibrationPose(userId, bodyIndex, playerCalibrationPose, gestureManager, ref alTrackedBodies))
                {
                    //int uidIndex = alUserIds.Count;
                    int uidIndex = GetEmptyUserSlot(userId, bodyIndex, ref alTrackedBodies, userDetectionOrder);

                    if (uidIndex >= 0)
                    {
                        aUserIndexIds[uidIndex] = userId;
                    }
                    else
                    {
                        // no empty user-index slot
                        return(-1);
                    }

                    dictUserIdToIndex[userId] = bodyIndex;
                    dictUserIdToTime[userId]  = Time.time;
                    alUserIds.Add(userId);

                    // set primary user-id, if there is none
                    if (liPrimaryUserId == 0 && aUserIndexIds.Length > 0)
                    {
                        liPrimaryUserId = aUserIndexIds[0];  // userId
                    }

                    return(uidIndex);
                }
            }

            return(-1);
        }
        /// <summary>
        /// Rearranges the user indices, according to the current criteria
        /// </summary>
        public virtual void RearrangeUserIndices(KinectManager.UserDetectionOrder userDetectionOrder)
        {
            if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
            {
                // get current user positions
                Vector3[] userPos = new Vector3[aUserIndexIds.Length];
                for (int i = 0; i < aUserIndexIds.Length; i++)
                {
                    ulong userId = aUserIndexIds[i];
                    userPos[i] = userId != 0 ? kinectManager.GetUserPosition(userId) : Vector3.zero;
                }

                // bubble sort
                bool reorderDone = false;
                for (int i = aUserIndexIds.Length - 1; i >= 1; i--)
                {
                    bool switchDone = false;

                    for (int j = 0; j < i; j++)
                    {
                        float userDist1 = 0f;
                        if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
                        {
                            userDist1 = Mathf.Abs(userPos[j].x) + Mathf.Abs(userPos[j].z);
                        }
                        else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
                        {
                            userDist1 = userPos[j].x;
                        }

                        if (Mathf.Abs(userDist1) < 0.01f)
                        {
                            userDist1 = 1000f;  // far away
                        }
                        float userDist2 = 0f;
                        if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
                        {
                            userDist2 = Mathf.Abs(userPos[j + 1].x) + Mathf.Abs(userPos[j + 1].z);
                        }
                        else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
                        {
                            userDist2 = userPos[j + 1].x;
                        }

                        if (Mathf.Abs(userDist2) < 0.01f)
                        {
                            userDist2 = 1000f;  // far away
                        }
                        if (userDist1 > userDist2)
                        {
                            // switch them
                            ulong tmpUserId = aUserIndexIds[j];
                            aUserIndexIds[j]     = aUserIndexIds[j + 1];
                            aUserIndexIds[j + 1] = tmpUserId;

                            reorderDone = switchDone = true;
                        }
                    }

                    if (!switchDone)  // check for sorted array
                    {
                        break;
                    }
                }

                if (reorderDone)
                {
                    System.Text.StringBuilder sbUsersOrder = new System.Text.StringBuilder();
                    sbUsersOrder.Append("Users reindexed: ");

                    for (int i = 0; i < aUserIndexIds.Length; i++)
                    {
                        if (aUserIndexIds[i] != 0)
                        {
                            sbUsersOrder.Append(i).Append(":").Append(aUserIndexIds[i]).Append("  ");
                        }
                    }

                    Debug.Log(sbUsersOrder.ToString());
                }
            }
        }
        // Returns empty user slot for the given user Id
        protected virtual int GetEmptyUserSlot(ulong userId, int bodyIndex, ref KinectInterop.BodyData[] alTrackedBodies, KinectManager.UserDetectionOrder userDetectionOrder)
        {
            // rearrange current users
            RearrangeUserIndices(userDetectionOrder);
            int uidIndex = -1;

            if (userDetectionOrder != KinectManager.UserDetectionOrder.Appearance)
            {
                // add the new user, depending on the distance
                Vector3 userPos = alTrackedBodies[bodyIndex].position;

                float userDist = 0f;
                if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
                {
                    userDist = Mathf.Abs(userPos.x) + Mathf.Abs(userPos.z);
                }
                else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
                {
                    userDist = userPos.x;
                }

                for (int i = 0; i < aUserIndexIds.Length; i++)
                {
                    if (aUserIndexIds[i] == 0)
                    {
                        // free user slot
                        uidIndex = i;
                        break;
                    }
                    else
                    {
                        ulong   uidUserId  = aUserIndexIds[i];
                        Vector3 uidUserPos = kinectManager.GetUserPosition(uidUserId);

                        float uidUserDist = 0;
                        if (userDetectionOrder == KinectManager.UserDetectionOrder.Distance)
                        {
                            uidUserDist = Mathf.Abs(uidUserPos.x) + Mathf.Abs(uidUserPos.z);
                        }
                        else if (userDetectionOrder == KinectManager.UserDetectionOrder.LeftToRight)
                        {
                            uidUserDist = uidUserPos.x;
                        }

                        if (userDist < uidUserDist)
                        {
                            // current user is left to the compared one
                            for (int u = (aUserIndexIds.Length - 2); u >= i; u--)
                            {
                                aUserIndexIds[u + 1] = aUserIndexIds[u];

                                if (aUserIndexIds[u] != 0)
                                {
                                    Debug.Log(string.Format("Reindexing user {0} to {1}, ID: {2}, pos: {3}, other-pos: {4}.", u, u + 1, aUserIndexIds[u], userPos, uidUserPos));
                                }
                            }

                            aUserIndexIds[i] = 0; // cleanup current index
                            uidIndex         = i;
                            break;
                        }
                    }
                }
            }
            else
            {
                // look for the 1st available slot
                for (int i = 0; i < aUserIndexIds.Length; i++)
                {
                    if (aUserIndexIds[i] == 0)
                    {
                        uidIndex = i;
                        break;
                    }
                }
            }

            return(uidIndex);
        }