Imports System

Imports Microsoft.Kinect

Namespace WaveDetection
    Public Class WaveGesture
#Region "メンバー変数"
        Private Const WAVE_THRESHOLD As Single = 0.1F
        Private Const WAVE_MOVEMENT_TIMEOUT As Integer = 5000
        Private Const LEFT_HAND As Integer = 0
        Private Const RIGHT_HAND As Integer = 1
        Private Const REQUIRED_ITERATIONS As Integer = 4


        Private _PlayerWaveTracker(5, 1) As WaveGestureTracker

        Public Event GestureDetected As EventHandler
#End Region


#Region "メソッド"
        Public Sub Update(skeletons() As Skeleton, frameTimestamp As Long)
            If (skeletons IsNot Nothing) Then
                Dim _skeleton As Skeleton

                For i As Integer = 0 To skeletons.Length - 1
                    _skeleton = skeletons(i)

                    If (_skeleton.TrackingState <> SkeletonTrackingState.NotTracked) Then
                        TrackWave(_skeleton, True, Me._PlayerWaveTracker(i, LEFT_HAND), frameTimestamp)
                        TrackWave(_skeleton, False, Me._PlayerWaveTracker(i, RIGHT_HAND), frameTimestamp)
                    Else
                        Me._PlayerWaveTracker(i, LEFT_HAND).Reset()
                        Me._PlayerWaveTracker(i, RIGHT_HAND).Reset()
                    End If
                Next
            End If
        End Sub

        Private Sub TrackWave(_skeleton As Skeleton,
                              isLeft As Boolean,
                              ByRef tracker As WaveGestureTracker,
                              timestamp As Long)
            Dim handJointId As JointType = IIf(isLeft, JointType.HandLeft, JointType.HandRight)
            Dim elbowJointId As JointType = IIf(isLeft, JointType.ElbowLeft, JointType.ElbowRight)
            Dim hand As Joint = _skeleton.Joints(handJointId)
            Dim elbow As Joint = _skeleton.Joints(elbowJointId)


            If (hand.TrackingState <> JointTrackingState.NotTracked AndAlso
               elbow.TrackingState <> JointTrackingState.NotTracked) Then
                If (tracker.State = WaveGestureState.InProgress AndAlso
                   tracker.Timestamp + WAVE_MOVEMENT_TIMEOUT < timestamp) Then
                    tracker.UpdateState(WaveGestureState.Failure, timestamp)
                    System.Diagnostics.Debug.WriteLine("Fail!")
                ElseIf (hand.Position.Y > elbow.Position.Y) Then
                    '画面の中心が(0, 0)となる値を使用する
                    'ユーザーから見てX軸の負の値が左に、
                    '正の値が右になるようにする
                    If (hand.Position.X <= elbow.Position.X - WAVE_THRESHOLD) Then
                        tracker.UpdatePosition(WavePosition.Left, timestamp)
                    ElseIf (hand.Position.X >= elbow.Position.X + WAVE_THRESHOLD) Then
                        tracker.UpdatePosition(WavePosition.Right, timestamp)
                    Else
                        tracker.UpdatePosition(WavePosition.Neutral, timestamp)
                    End If

                    If (tracker.State <> WaveGestureState.Success AndAlso
                       tracker.IterationCount = REQUIRED_ITERATIONS) Then
                        tracker.UpdateState(WaveGestureState.Success, timestamp)
                        System.Diagnostics.Debug.WriteLine("Success!")

                        RaiseEvent GestureDetected(Me, New EventArgs())
                    End If
                Else
                    If (tracker.State = WaveGestureState.InProgress) Then
                        tracker.UpdateState(WaveGestureState.Failure, timestamp)
                        System.Diagnostics.Debug.WriteLine("Fail!")
                    Else
                        tracker.Reset()
                    End If
                End If
            Else
                tracker.Reset()
            End If
        End Sub
#End Region


#Region "ヘルパーオブジェクト"
        Private Enum WavePosition
            None = 0
            Left = 1
            Right = 2
            Neutral = 3
        End Enum


        Private Enum WaveGestureState
            None = 0
            Success = 1
            Failure = 2
            InProgress = 3
        End Enum


        Private Structure WaveGestureTracker
            Public IterationCount As Integer
            Public State As WaveGestureState
            Public Timestamp As Long
            Public StartPosition As WavePosition
            Public CurrentPosition As WavePosition


            Public Sub UpdateState(_state As WaveGestureState, _timestamp As Long)
                Me.State = _state
                Me.Timestamp = _timestamp
            End Sub


            Public Sub Reset()
                Me.IterationCount = 0
                Me.State = WaveGestureState.None
                Me.Timestamp = 0
                Me.StartPosition = WavePosition.None
                Me.CurrentPosition = WavePosition.None
            End Sub


            Public Sub UpdatePosition(position As WavePosition, _timestamp As Long)
                If (Me.CurrentPosition <> position) Then
                    If (position = WavePosition.Left OrElse
                       position = WavePosition.Right) Then
                        If (Me.State <> WaveGestureState.InProgress) Then
                            Me.State = WaveGestureState.InProgress
                            Me.IterationCount = 0
                            Me.StartPosition = position
                        End If

                        Me.IterationCount += 1
                    End If

                    Me.CurrentPosition = position
                    Me.Timestamp = _timestamp
                End If
            End Sub
        End Structure
#End Region
    End Class
End Namespace