Imports System
Imports System.Linq
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Shapes
Imports System.ComponentModel
Imports Microsoft.Kinect
Imports Microsoft.Speech.Recognition
Imports System.IO
Imports Microsoft.Speech.AudioFormat

''' <summary>
'''  Interaction logic for MainWindow.xaml
''' </summary>
''' <remarks></remarks>
Class MainWindow
    Implements INotifyPropertyChanged

    Private _kinectSensor As KinectSensor
    Private _sre As SpeechRecognitionEngine
    Private _source As KinectAudioSource

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        Me.DataContext = Me
        Me.DataContext = Me
        AddHandler Me.Unloaded, Sub()
                                    _kinectSensor.SkeletonStream.Disable()
                                    _sre.RecognizeAsyncCancel()
                                    _sre.RecognizeAsyncStop()
                                    _sre.Dispose()
                                End Sub

        AddHandler Me.Loaded, Sub()
                                  _kinectSensor = KinectSensor.KinectSensors(0)
                                  _kinectSensor.SkeletonStream.Enable(New TransformSmoothParameters() With {
                                                                      .Correction = 0.5F,
                                                                      .JitterRadius = 0.05F,
                                                                      .MaxDeviationRadius = 0.04F,
                                                                      .Smoothing = 0.5F})
                                  AddHandler _kinectSensor.SkeletonFrameReady, AddressOf nui_SkeletonFrameReady
                                  _kinectSensor.Start()
                                  StartSpeechRecognition()
                              End Sub
    End Sub

    Sub nui_SkeletonFrameReady(sender As Object, e As SkeletonFrameReadyEventArgs)
        Using _skeletonFrame As SkeletonFrame = e.OpenSkeletonFrame()
            If (_skeletonFrame Is Nothing) Then
                Exit Sub
            End If

            Dim skeletons(_skeletonFrame.SkeletonArrayLength - 1) As Skeleton
            _skeletonFrame.CopySkeletonDataTo(skeletons)
            For Each skeletonData As Skeleton In skeletons
                If (skeletonData.TrackingState = SkeletonTrackingState.Tracked) Then
                    Dim rightHandVec As Microsoft.Kinect.SkeletonPoint = skeletonData.Joints(JointType.HandRight).Position
                    Dim depthPoint As DepthImagePoint = _kinectSensor.MapSkeletonPointToDepth(rightHandVec,
                                                                                              DepthImageFormat.Resolution640x480Fps30)
                    HandTop = depthPoint.Y * Me.MainStage.ActualHeight / 480
                    HandLeft = depthPoint.X * Me.MainStage.ActualWidth / 640
                End If
            Next
        End Using
    End Sub

    Private Function CreateAudioSource() As KinectAudioSource
        Dim source As KinectAudioSource = KinectSensor.KinectSensors(0).AudioSource
        source.AutomaticGainControlEnabled = False
        source.EchoCancellationMode = EchoCancellationMode.None
        Return source
    End Function

    Private Sub StartSpeechRecognition()
        _source = CreateAudioSource()
        Dim matchingFunc As Func(Of RecognizerInfo, Boolean) = Function(r)
                                                                   Dim value As String
                                                                   r.AdditionalInfo.TryGetValue("Kinect", value)
                                                                   Return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) _
                                                                       AndAlso "ja-JP".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase)
                                                               End Function
        Dim ri As RecognizerInfo = SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault()

        _sre = New SpeechRecognitionEngine(ri.Id)
        CreateGrammars(ri)
        AddHandler _sre.SpeechRecognized, AddressOf sre_SpeechRecognized
        AddHandler _sre.SpeechHypothesized, AddressOf sre_SpeechHypothesized
        AddHandler _sre.SpeechRecognitionRejected, AddressOf sre_SpeechRecognitionRejected

        Dim s As Stream = _source.Start()
        _sre.SetInputToAudioStream(s,
                                   New SpeechAudioFormatInfo(
                                       EncodingFormat.Pcm, 16000, 16, 1,
                                       32000, 2, Nothing))
        _sre.RecognizeAsync(RecognizeMode.Multiple)
    End Sub

    ' 監修者注:「ここに　あかの　さんかく　せっち」のように発声 
    Private Sub CreateGrammars(ri As RecognizerInfo)
        Dim colors As New Choices()
        colors.Add("シアンの")
        colors.Add("きいろの")
        colors.Add("マゼンタの")
        colors.Add("あおの")
        colors.Add("みどりの")
        colors.Add("あかの")

        Dim create As New Choices()
        create.Add("せいせい")
        create.Add("せっち")

        Dim shapes As New Choices()
        shapes.Add("まる")
        shapes.Add("さんかく")
        shapes.Add("しかく")
        shapes.Add("ひしがた")

        Dim gb As New GrammarBuilder() With {.Culture = ri.Culture}
        gb.Append("ここに")    'ここに
        gb.Append(colors)      'あおの
        gb.Append(shapes)      'はこ
        gb.Append(create)      '設置

        Dim g As New Grammar(gb)
        _sre.LoadGrammar(g)

        Dim q As New GrammarBuilder() With {.Culture = ri.Culture}
        q.Append("quit application")
        Dim quit As New Grammar(q)

        _sre.LoadGrammar(quit)
    End Sub

    Sub sre_SpeechRecognitionRejected(sender As Object, e As SpeechRecognitionRejectedEventArgs)
        HypothesizedText += " Rejected"
        Confidence = Math.Round(e.Result.Confidence, 2).ToString()
    End Sub

    Sub sre_SpeechHypothesized(sender As Object, e As SpeechHypothesizedEventArgs)
        HypothesizedText = e.Result.Text
    End Sub

    Sub sre_SpeechRecognized(sender As Object, e As SpeechRecognizedEventArgs)
        Dim method As Action(Of SpeechRecognizedEventArgs) = AddressOf InterpretCommand
        Dispatcher.BeginInvoke(method, e)
    End Sub

    Private Sub InterpretCommand(e As SpeechRecognizedEventArgs)
        Dim result As RecognitionResult = e.Result
        Confidence = Math.Round(result.Confidence, 2).ToString()
        If (result.Words(0).Text = "quit" AndAlso result.Words(1).Text = "application") Then
            If (result.Confidence > 0.95) Then
                Me.Close()
            End If
            Exit Sub
        End If
        If (result.Confidence < 0.3) Then
            Exit Sub
        End If
        If (result.Words(3).Text = "せっち" OrElse
                result.Words(3).Text = "せいせい") Then
            Dim colorString As String = result.Words(1).Text
            Dim _color As Color
            Select Case colorString
                Case "シアンの"
                    _color = Colors.Cyan
                Case "きいろの"
                    _color = Colors.Yellow
                Case "マゼンタの"
                    _color = Colors.Magenta
                Case "あおの"
                    _color = Colors.Blue
                Case "みどりの"
                    _color = Colors.Green
                Case "あかの"
                    _color = Colors.Red
                Case Else
                    Exit Sub
            End Select

            Dim shapeString As String = result.Words(2).Text
            Dim _shape As Shape
            Select Case shapeString
                Case "まる"
                    _shape = New Ellipse()
                    _shape.Width = 150
                    _shape.Height = 150
                Case "しかく"
                    _shape = New Rectangle()
                    _shape.Width = 150
                    _shape.Height = 150
                Case "さんかく"
                    Dim poly As New Polygon()
                    poly.Points.Add(New Point(0, 0))
                    poly.Points.Add(New Point(150, 0))
                    poly.Points.Add(New Point(75, -150))
                    _shape = poly
                Case "ひしがた"
                    Dim poly2 As New Polygon()
                    poly2.Points.Add(New Point(0, 0))
                    poly2.Points.Add(New Point(75, 150))
                    poly2.Points.Add(New Point(150, 0))
                    poly2.Points.Add(New Point(75, -150))
                    _shape = poly2
                Case Else
                    Exit Sub
            End Select
            _shape.SetValue(Canvas.LeftProperty, HandLeft)
            _shape.SetValue(Canvas.TopProperty, HandTop)
            _shape.Fill = New SolidColorBrush(_color)
            MainStage.Children.Add(_shape)
        End If
    End Sub

    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Private Sub NotifyPropertyChanged(propertyName As String)
        Dim handler As PropertyChangedEventHandler = PropertyChangedEvent
        If Nothing IsNot handler Then
            handler(Me, New PropertyChangedEventArgs(propertyName))
        End If
    End Sub

    Private _handLeft As Double
    Public Property HandLeft As Double
        Get
            Return _handLeft
        End Get
        Set(value As Double)
            _handLeft = value
            NotifyPropertyChanged("HandLeft")
        End Set
    End Property

    Private _handTop As Double
    Public Property HandTop As Double
        Get
            Return _HandTop
        End Get
        Set(value As Double)
            _handTop = value
            NotifyPropertyChanged("HandTop")
        End Set
    End Property

    Private _hypothesizedText As String
    Public Property HypothesizedText As String
        Get
            Return _hypothesizedText
        End Get
        Set(value As String)
            _hypothesizedText = value
            NotifyPropertyChanged("HypothesizedText")
        End Set
    End Property

    Private _confidence As String
    Public Property Confidence As String
        Get
            Return _confidence
        End Get
        Set(value As String)
            _confidence = value
            NotifyPropertyChanged("Confidence")
        End Set
    End Property
End Class
