Imports System
Imports Beginning.Kinect.Framework.Input
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Controls

Namespace Controls
    Public Class MagnetButton
        Inherits HoverButton

        Protected _isLockOn As Boolean = True
        Public Shared ReadOnly KinectCursorLockEvent As RoutedEvent = KinectInput.KinectCursorUnlockEvent.AddOwner(GetType(MagnetButton))
        Public Shared ReadOnly KinectCursorUnlockEvent As RoutedEvent = KinectInput.KinectCursorLockEvent.AddOwner(GetType(MagnetButton))

        Public Sub New()

        End Sub

        Private _KinectCursorLock As KinectCursorEventHandler
        Public Custom Event KinectCursorLock As KinectCursorEventHandler
            AddHandler(value As KinectCursorEventHandler)
                Me._KinectCursorLock = CType(System.Delegate.Combine(Me._KinectCursorLock, value), KinectCursorEventHandler)
            End AddHandler

            RemoveHandler(value As KinectCursorEventHandler)
                Me._KinectCursorLock = CType(System.Delegate.Remove(Me._KinectCursorLock, value), KinectCursorEventHandler)
            End RemoveHandler

            RaiseEvent(sender As Object, e As EventArgs)
                If Not Me._KinectCursorLock Is Nothing Then
                    Me._KinectCursorLock(sender, e)
                End If
            End RaiseEvent
        End Event

        Private _KinectCursorUnlock As KinectCursorEventHandler
        Public Custom Event KinectCursorUnlock As KinectCursorEventHandler
            AddHandler(value As KinectCursorEventHandler)
                Me._KinectCursorUnlock = CType(System.Delegate.Combine(Me._KinectCursorUnlock, value), KinectCursorEventHandler)
            End AddHandler

            RemoveHandler(value As KinectCursorEventHandler)
                Me._KinectCursorUnlock = CType(System.Delegate.Remove(Me._KinectCursorUnlock, value), KinectCursorEventHandler)
            End RemoveHandler

            RaiseEvent(sender As Object, e As EventArgs)
                If Not Me._KinectCursorUnlock Is Nothing Then
                    Me._KinectCursorUnlock(sender, e)
                End If
            End RaiseEvent
        End Event

        ''' <summary>
        ''' Gets or sets the amount of time it takes the cursor to lock into place.
        ''' </summary>
        ''' <value>
        ''' The lock interval.
        ''' </value>
        Public Property LockInterval() As Double
            Get
                Return CType(GetValue(LockIntervalProperty), Double)
            End Get
            Set(value As Double)
                SetValue(LockIntervalProperty, value)
            End Set
        End Property

        ' Using a DependencyProperty as the backing store for LockInterval.  Me enables animation, styling, binding, etc...
        Public Shared ReadOnly LockIntervalProperty As DependencyProperty =
            DependencyProperty.Register("LockInterval",
                                        GetType(Double),
                                        GetType(MagnetButton),
                                        New UIPropertyMetadata(200.0R))


        Public Property UnlockInterval As Double
            Get
                Return CType(GetValue(UnlockIntervalProperty), Double)
            End Get
            Set(value As Double)
                SetValue(UnlockIntervalProperty, value)
            End Set
        End Property

        ' Using a DependencyProperty as the backing store for UnlockInterval.  Me enables animation, styling, binding, etc...
        Public Shared ReadOnly UnlockIntervalProperty As DependencyProperty =
            DependencyProperty.Register("UnlockInterval",
                                        GetType(Double),
                                        GetType(MagnetButton),
                                        New UIPropertyMetadata(80.0R))

        Public Property LockXOffsetFromCenter As Double
            Get
                Return CType(GetValue(LockXOffsetFromCenterProperty), Double)
            End Get
            Set(value As Double)
                SetValue(LockXOffsetFromCenterProperty, value)
            End Set
        End Property

        ' Using a DependencyProperty as the backing store for LockXOffsetFromCenter.  Me enables animation, styling, binding, etc...
        Public Shared ReadOnly LockXOffsetFromCenterProperty As DependencyProperty =
            DependencyProperty.Register("LockXOffsetFromCenter",
                                        GetType(Double),
                                        GetType(MagnetButton),
                                        New UIPropertyMetadata(0.0R))

        Public Property LockYOffsetFromCenter As Double
            Get
                Return CType(GetValue(LockYOffsetFromCenterProperty), Double)
            End Get
            Set(value As Double)
                SetValue(LockYOffsetFromCenterProperty, value)
            End Set
        End Property

        ' Using a DependencyProperty as the backing store for LockXOffsetFromCenter.  Me enables animation, styling, binding, etc...
        Public Shared ReadOnly LockYOffsetFromCenterProperty As DependencyProperty =
            DependencyProperty.Register("LockYOffsetFromCenter",
                                        GetType(Double),
                                        GetType(MagnetButton),
                                        New UIPropertyMetadata(0.0R))

        Private move As Storyboard

        Protected Overrides Sub OnKinectCursorEnter(sender As Object, e As KinectCursorEventArgs)
            If (Me.Opacity = 0) Then
                Exit Sub
            End If
            'Debug.WriteLine("Enter ")
            If (Not _isLockOn) Then
                Exit Sub
            End If
            ' get button position
            Dim rootVisual = FindAncestor(Of Window)(Me)
            Dim _point = Me.TransformToAncestor(rootVisual).Transform(New Point(0, 0))

            Dim x As Double = _point.X + Me.ActualWidth / 2
            Dim y As Double = _point.Y + Me.ActualHeight / 2

            Dim cursor = e.Cursor
            cursor.UpdateCursor(New Point(e.X, e.Y), True)

            ' find target position
            Dim lockPoint As New Point(x - cursor.CursorVisual.ActualWidth / 2 + LockXOffsetFromCenter, y - cursor.CursorVisual.ActualHeight / 2 + LockYOffsetFromCenter)

            ' find current location
            Dim cursorPoint As New Point(e.X - cursor.CursorVisual.ActualWidth / 2, e.Y - cursor.CursorVisual.ActualHeight / 2)

            ' guide cursor to its final position
            Dim moveLeft As New DoubleAnimation(cursorPoint.X, lockPoint.X, New Duration(TimeSpan.FromMilliseconds(LockInterval)))
            Storyboard.SetTarget(moveLeft, cursor.CursorVisual)
            Storyboard.SetTargetProperty(moveLeft, New PropertyPath(Canvas.LeftProperty))
            Dim moveTop As New DoubleAnimation(cursorPoint.Y, lockPoint.Y, New Duration(TimeSpan.FromMilliseconds(LockInterval)))
            Storyboard.SetTarget(moveTop, cursor.CursorVisual)
            Storyboard.SetTargetProperty(moveTop, New PropertyPath(Canvas.TopProperty))
            move = New Storyboard()
            move.Children.Add(moveTop)
            move.Children.Add(moveLeft)

            AddHandler move.Completed, Sub()
                                           Me.RaiseEvent(New KinectCursorEventArgs(KinectCursorLockEvent,
                                                                                   New Point(x + LockXOffsetFromCenter,
                                                                                             y + LockYOffsetFromCenter),
                                                                                         e.Z) With {.Cursor = e.Cursor})
                                       End Sub
            If (move IsNot Nothing) Then
                move.Stop(e.Cursor)
            End If
            move.Begin(cursor, False)
            MyBase.OnKinectCursorEnter(sender, e)
        End Sub


        Protected Overrides Sub OnKinectCursorLeave(sender As Object, e As KinectCursorEventArgs)
            If (Me.Opacity = 0) Then
                Return
            End If
            'Debug.WriteLine("Leave ")
            MyBase.OnKinectCursorLeave(sender, e)
            If (Not _isLockOn) Then
                Exit Sub
            End If

            'if(move != null)
            '    move.Stop(e.Cursor)

            e.Cursor.UpdateCursor(New Point(e.X, e.Y), False)
            'get button position
            Dim rootVisual = FindAncestor(Of Window)(Me)
            Dim _point = Me.TransformToAncestor(rootVisual).Transform(New Point(0, 0))

            Dim x As Double = _point.X + Me.ActualWidth / 2
            Dim y As Double = _point.Y + Me.ActualHeight / 2

            Dim cursor = e.Cursor
            ' find target position
            Dim lockPoint As New Point(x - cursor.CursorVisual.ActualWidth / 2 + LockXOffsetFromCenter, y - cursor.CursorVisual.ActualHeight / 2 + LockYOffsetFromCenter)

            ' find current location
            Dim cursorPoint As New Point(e.X - cursor.CursorVisual.ActualWidth / 2, e.Y - cursor.CursorVisual.ActualHeight / 2)

            ' guide cursor to its final position
            Dim moveLeft As New DoubleAnimation(lockPoint.X, cursorPoint.X, New Duration(TimeSpan.FromMilliseconds(UnlockInterval)))
            Storyboard.SetTarget(moveLeft, cursor.CursorVisual)
            Storyboard.SetTargetProperty(moveLeft, New PropertyPath(Canvas.LeftProperty))
            Dim moveTop As New DoubleAnimation(lockPoint.Y, cursorPoint.Y, New Duration(TimeSpan.FromMilliseconds(UnlockInterval)))
            Storyboard.SetTarget(moveTop, cursor.CursorVisual)
            Storyboard.SetTargetProperty(moveTop, New PropertyPath(Canvas.TopProperty))
            move = New Storyboard()
            move.Children.Add(moveTop)
            move.Children.Add(moveLeft)
            AddHandler move.Completed, Sub()
                                           move.Stop(cursor)
                                           cursor.UpdateCursor(New Point(e.X, e.Y), False)
                                           Me.RaiseEvent(New KinectCursorEventArgs(KinectCursorUnlockEvent,
                                                                                   New Point(e.X,
                                                                                             e.Y),
                                                                                         e.Z) With {.Cursor = e.Cursor})
                                       End Sub
            move.Begin(cursor, True)
        End Sub

        Private _lastPointDetected As KinectCursorEventArgs
        Protected Overrides Sub OnKinectCursorMove(sender As Object, e As KinectCursorEventArgs)
            _lastPointDetected = e
        End Sub

        Protected Overrides Sub OnKinectCursorDeactivated(sender As Object, e As RoutedEventArgs)
            'Debug.WriteLine("deactivated")
            Me.RaiseEvent(New KinectCursorEventArgs(KinectCursorUnlockEvent,
                                                    New Point(_lastPointDetected.X,
                                                              _lastPointDetected.Y),
                                                          _lastPointDetected.Z) With {.Cursor = _lastPointDetected.Cursor})
        End Sub

        Protected Overrides Sub OnKinectCursorActivated(sender As Object, e As RoutedEventArgs)
            'Debug.WriteLine("Activated")
            Me.RaiseEvent(New KinectCursorEventArgs(KinectCursorEnterEvent,
                                                    New Point(_lastPointDetected.X,
                                                              _lastPointDetected.Y),
                                                          _lastPointDetected.Z) With {.Cursor = _lastPointDetected.Cursor})
        End Sub

        Private Function FindAncestor(Of T As Class)(_dependencyObject As DependencyObject) As T
            Dim target As DependencyObject = _dependencyObject
            Do
                target = VisualTreeHelper.GetParent(target)
            Loop While (target IsNot Nothing AndAlso Not (TypeOf target Is T))
            Return TryCast(target, T)
        End Function
    End Class
End Namespace