Option Strict On

Imports Mbark.Threading
Imports Mbark.UI
Imports System.Drawing
Imports System.Delegate
Imports System.Threading
Imports System.Runtime.InteropServices

Imports Mbark.IADeviceControlDefs
Imports Iris
Imports Tool
Imports DeviceControl

Namespace Mbark.Sensors
    Public Class LGIrisSensor
        Inherits BaseSensor
        Private Const MinimumFocusValue As Integer = 90
        Private Const DefaultVolume As Integer = 1
        Const LGManufacturer As String = "LG"
        Const IrisAccess3000 As String = "LG IrisAccess EOU3000"

#Region " Windows Form Designer generated code "

        Public Sub New()
            MyBase.New()

            'This call is required by the Windows Form Designer.
            InitializeComponent()

            'Add any initialization after the InitializeComponent() call
            CreateNew()
        End Sub

        'UserControl overrides dispose to clean up the component list.
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub

        'Required by the Windows Form Designer
        Private components As System.ComponentModel.IContainer

        'NOTE: The following procedure is required by the Windows Form Designer
        'It can be modified using the Windows Form Designer.  
        'Do not modify it using the code editor.
        Friend WithEvents pbrFocusValue As System.Windows.Forms.ProgressBar
        Friend WithEvents TimeOutCounter As System.Windows.Forms.Timer
        Friend WithEvents IrisImageBox As System.Windows.Forms.PictureBox
        <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
            Me.components = New System.ComponentModel.Container
            Me.pbrFocusValue = New System.Windows.Forms.ProgressBar
            Me.TimeOutCounter = New System.Windows.Forms.Timer(Me.components)
            Me.IrisImageBox = New System.Windows.Forms.PictureBox
            Me.MainPanel.SuspendLayout()
            Me.InnerPanel.SuspendLayout()
            CType(Me.OuterPanel, System.ComponentModel.ISupportInitialize).BeginInit()
            Me.OuterPanel.SuspendLayout()
            Me.SuspendLayout()
            '
            'CancelSensorButton
            '
            Me.CancelSensorButton.Name = "CancelSensorButton"
            '
            'ActivateSensorButton
            '
            Me.ActivateSensorButton.Name = "ActivateSensorButton"
            '
            'TimeoutIndicator
            '
            Me.TimeoutIndicator.Name = "TimeoutIndicator"
            Me.TimeoutIndicator.Size = New System.Drawing.Size(382, 16)
            '
            'MainPanel
            '
            Me.MainPanel.Anchor = System.Windows.Forms.AnchorStyles.None
            Me.MainPanel.Controls.Add(Me.IrisImageBox)
            Me.MainPanel.Controls.Add(Me.pbrFocusValue)
            Me.MainPanel.Location = New System.Drawing.Point(96, 104)
            Me.MainPanel.Name = "MainPanel"
            Me.MainPanel.Size = New System.Drawing.Size(400, 240)
            '
            'InnerPanel
            '
            Me.InnerPanel.Name = "InnerPanel"
            Me.InnerPanel.Size = New System.Drawing.Size(382, 278)
            '
            'OuterPanel
            '
            Me.OuterPanel.Name = "OuterPanel"
            Me.OuterPanel.Size = New System.Drawing.Size(384, 360)
            '
            'pbrFocusValue
            '
            Me.pbrFocusValue.Dock = System.Windows.Forms.DockStyle.Bottom
            Me.pbrFocusValue.Location = New System.Drawing.Point(0, 224)
            Me.pbrFocusValue.Name = "pbrFocusValue"
            Me.pbrFocusValue.Size = New System.Drawing.Size(400, 16)
            Me.pbrFocusValue.TabIndex = 5
            '
            'IrisImageBox
            '
            Me.IrisImageBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
            Me.IrisImageBox.Dock = System.Windows.Forms.DockStyle.Fill
            Me.IrisImageBox.Location = New System.Drawing.Point(0, 0)
            Me.IrisImageBox.Name = "IrisImageBox"
            Me.IrisImageBox.Size = New System.Drawing.Size(400, 224)
            Me.IrisImageBox.TabIndex = 9
            Me.IrisImageBox.TabStop = False
            '
            'LGIrisCtrl
            '
            Me.Name = "LGIrisCtrl"
            Me.Size = New System.Drawing.Size(384, 360)
            Me.MainPanel.ResumeLayout(False)
            Me.InnerPanel.ResumeLayout(False)
            CType(Me.OuterPanel, System.ComponentModel.ISupportInitialize).EndInit()
            Me.OuterPanel.ResumeLayout(False)
            Me.ResumeLayout(False)

        End Sub

#End Region
        Protected Delegate Function ProcessDelegate() As Object
        Protected Delegate Function CaptureDelegate(ByVal CmdGuid As Guid) As Object

        Public Sub CreateNew()
            MyBase.WritableDeferInitialization = True
            MyBase.TimeoutIndicator.CriticalTime = 3 * 1000
        End Sub

        Private mActive As Boolean = False
        Public Overrides ReadOnly Property IsActive() As Boolean
            Get
                Return mActive
            End Get
        End Property
        Private mModality As SensorModality = SensorModality.Iris
        Public Overrides ReadOnly Property Modality() As SensorModality
            Get
                Return mModality
            End Get
        End Property
        Private mName As String = "LG Iris Access"
        Public Overrides ReadOnly Property FriendlyName() As String
            Get
                Return mName
            End Get
        End Property

        Private Property FocusValue() As Integer
            Get
                Return pbrFocusValue.Value
            End Get
            Set(ByVal Value As Integer)
                pbrFocusValue.Value = Value
                pbrFocusValue.Refresh()
            End Set
        End Property
        Private mThumbnail As Bitmap

#Region "       Initialization         "
        Public Overrides ReadOnly Property InitializationCommandTemplate() As Threading.AsyncCommandTemplate
            Get
                Static init As AsyncCommandTemplate
                If init Is Nothing Then
                    init = New AsyncCommandTemplate
                    With init
                        .TargetMethod = CreateDelegate(GetType(ProcessDelegate), Me, "Initialization")
                        .ExpirationTime = 10000 '10 seconds
                        .IgnoreUnderlyingHandleOfTargetControl = True
                    End With
                End If
                Return init
            End Get
        End Property
        'Initialization helper function 
        Private Function Initialization() As Object
            Debugging.WriteLine(DateTime.Now.TimeOfDay.TotalMilliseconds & " LG on " & System.Threading.Thread.CurrentThread.Name)
            Dim type As Integer = IADeviceControlDefs.IAOU3000_TYPE
            WritableRequiresRecovery = False
            Try
                LGOU.Open(1, type, 1)
                MarkAsOnline()
                SensorProperties()
            Catch ex As Exception
                MarkAsOffline()
                WritableRequiresRecovery = True
                Throw New InitializationFailureException("LG: Initialization failed.", ex)
            End Try
            lProgramStatus = IDLE_STATE
        End Function
        Private mSensorProperties As New SensorProperties
        Private Sub SensorProperties()
            With LGIris
                mSensorProperties.Manufacturer = LGManufacturer
                mSensorProperties.ModelName = IrisAccess3000
                Dim FirmwareVersion As Single
                LGOU.GetVersion(FirmwareVersion)
                mSensorProperties.FirmwareVersion = CStr(FirmwareVersion)
                mSensorProperties.VendorLibraryName = "IAIRISLib"
                Dim VendorVersion As Single
                LGIris.GetVersion(VendorVersion)
                mSensorProperties.VendorLibraryVersion = CStr(VendorVersion)
                mSensorProperties.Modality = Modality
            End With

        End Sub
#End Region

#Region "         Configuration         "
        Public Overrides ReadOnly Property ConfigurationCommandTemplate() As Threading.AsyncCommandTemplate
            Get
                Static config As AsyncCommandTemplate
                If config Is Nothing Then
                    config = New AsyncCommandTemplate
                    With config
                        .ExpirationTime = 10000
                        .IgnoreUnderlyingHandleOfTargetControl = True
                    End With
                End If
                Return config
            End Get
        End Property
        Public Sub Configuration(ByVal ConfigurationObj As Object)

        End Sub
#End Region

#Region "       Uninitialization        "
        Public Overrides Sub Uninitialize()
            Try
                LGOU.Close()
            Catch ex As Exception
            End Try
            MyBase.Uninitialize()
            lProgramStatus = CLOSE_STATE
        End Sub
#End Region

#Region "       Capture         "
        Public Overrides ReadOnly Property CaptureCommandTemplate() As Threading.AsyncCommandTemplate
            Get
                Static Capture As AsyncCommandTemplate
                If Capture Is Nothing Then
                    Capture = New AsyncCommandTemplate
                    With Capture
                        .TargetMethod = CreateDelegate(GetType(ProcessDelegate), Me, "CaptureImage")
                        .ExpirationTime = 20000
                        .IgnoreUnderlyingHandleOfTargetControl = True
                    End With
                End If
                Return Capture
            End Get
        End Property

        Private Function CaptureImage() As Object

            WritableLatestThumbnail = Nothing
            mThumbnail = Nothing
            WritablePollingWasCanceled = False
            WritableIsTimeout = False

            If EmptyPalette Is Nothing Then InitEmptyPalette()

            Dim vIrisImage, bSmallImage, LiveImage As Object
            Dim focusValue As Integer
            Dim distance As Integer
            lProgramStatus = CAPTURE_STATE

            InitializeLiveImageBox()

            Dim counter As Integer = 0
            Try
                LGOU.SetVolume(DefaultVolume)
                Dim StartTime As Double = DateTime.Now.TimeOfDay.TotalMilliseconds
                Dim ElapsedTime As Double

                LGOU.ControlSound(SOU_START_ENROLL)

                Do
                    WaitWithDoEvents(50, 10)

                    CheckRange(distance, IADeviceControlDefs.ENROLL_MODE)

                    'Capture Image 
                    LGOU.CaptureImage(ENROLL_MODE, lCAPTURE_TIME, 0, focusValue, vIrisImage)

                    If focusValue >= MinimumFocusValue Then LiveImage = GetIrisImage()
                    If Not LiveImage Is Nothing Then Exit Do

                    'Reduce captured Image (640*480 -> 320*240)
                    LGTool.GetSmallImage(vIrisImage, bSmallImage, 0)
                    IrisImageBox.Image = ConvertBytesAsObjectToBitmap(bSmallImage, 320, 240)
                    focusValue = focusValue
                    ElapsedTime = DateTime.Now.TimeOfDay.TotalMilliseconds - StartTime
                    If ElapsedTime > 18000.0 Then writableIstimeout = True
                Loop While (Not PollingWasCanceled) AndAlso (Not IsTimeout)

                'Must stop the CounterTimer soon after Capture returns from the reader
                StopCountdownTimerNow()

                ' if timed out or capture is cancelled, then throw exceptions
                If PollingWasCanceled Or IsTimeout Then
                    MarkAsOnline()
                    If PollingWasCanceled Then Throw New PollingCanceledException
                    If IsTimeout Then Throw New CaptureTimeoutException
                End If

                '-------- release array ---------------
                vIrisImage = Nothing
                Erase bSmallImage
                '-------- release array ---------------
                Dim IrisImage As Bitmap = ConvertBytesAsObjectToBitmap(LiveImage, 640, 480)
                MyBase.WritableLatestThumbnail = IrisImage

                Dim result As New CaptureResult
                result.ImageProperties.Image = IrisImage
                result.ImageProperties.Timestamp = DateTime.UtcNow
                result.SensorProperties = mSensorProperties
                Dim CaptureResultList As New CaptureResultCollection
                CaptureResultList.Results.Add(result)

                Me.LastReviewImageAcceptable = True
                MarkAsOnline()
                Return CaptureResultList
            Catch ex As ThreadInterruptedException
                MarkAsOnline()
                Throw New CaptureTimeoutException("LG: CaptureImage timeout.", ex)
            Catch ex As COMException
                MarkAsOffLine()
                Throw New SensorException("LG: Sensor Failed.", ex)
            Finally
                ResetCtrl()
            End Try
        End Function

        Private Function GetIrisImage() As Object

            Dim lWhichIlluminator As Integer
            'store the right illuminator iris code
            Dim RightIrisCode As Object
            'store the left illuminator iris code
            Dim LeftIrisCode As Object
            Dim LiveImage As Object
            Dim RightIrisQuality As Integer
            Dim LeftIrisQuality As Integer
            'Index on best captured image with the right illuminator : 0~15
            Dim GoodRightImageIndex As Integer
            'Index on best captured image with the left illuminator : 16~31
            Dim GoodLeftImageIndex As Integer

            Dim GoodLiveImage(IMAGE_SIZE - 1) As Byte

            Try
                LGOU.ControlSound(SOU_START_ENROLL)

                '1 - enable detect fake eye, 0 - disable detect fake eye
                Dim lFakeEyeLevel_ForEnroll As Integer = 0

                Try
                    LGOU.GetIrisImage(ENROLL_MODE, lFakeEyeLevel_ForEnroll, TIME_TO_GET_IRISIMAGE, LiveImage, lWhichIlluminator)
                Catch ex As COMException When ex.ErrorCode = IADeviceControlDefs.IADC_ERROR_OUT_RANGE
                    Return Nothing
                End Try

                If LiveImage Is Nothing Then Return Nothing

                Try
                    LGIris.CreateIrisCode(IMAGE_NUMBER, LiveImage, _
                        RightIrisCode, LeftIrisCode, _
                        RightIrisQuality, LeftIrisQuality, _
                        GoodRightImageIndex, GoodLeftImageIndex)

                Catch ex As COMException When ex.errorcode = IAIrisdefs.IAIRIS_ERROR_BAD_IMAGE
                    ' Notice that LG has an error code that also correspond to  Win32 API error &H80010003 
                    ' "The caller is dispatching an intertask SendMessage call and cannot call out via PostMessage".
                    Return Nothing
                End Try

                LGOU.ControlIndicator(IND_ACCEPT_MODE)
                LGOU.ControlSound(SOU_ENROLLED)

                Dim goodImageIndex As Integer
                If TargetParts.Contains(BodyPart.LeftIris) Then
                    goodImageIndex = GoodLeftImageIndex
                Else
                    goodImageIndex = GoodRightImageIndex
                End If

                Try
                    Dim LiveImagesAsBytes() As Byte = CType(LiveImage, Byte())
                    Buffer.BlockCopy(LiveImagesAsBytes, goodImageIndex * IMAGE_SIZE, GoodLiveImage, 0, IMAGE_SIZE)
                Catch ex As Exception
                    Debugging.Break(ex)
                End Try

                Dim bSmallImage As Object
                LGTool.GetSmallImage(GoodLiveImage, bSmallImage, 0)

                Return GoodLiveImage
            Catch ex As COMException
                Throw New CaptureFailureException(String.Empty, ex)
                Return Nothing
            Catch ex As Exception
                Debugging.Break(ex)
                Return Nothing
            Finally

            End Try

        End Function
        Private Sub CheckRange(ByRef lRange As Integer, ByVal lVideoMode As Integer)
            Try
                LGOU.GetRange(lVideoMode, lRange)
                'Too Near Range 
                If (lRange = TOO_NEAR_RANGE) Then
                    If lProgramStatus = CAPTURE_STATE Then InitializeLiveImageBox()
                    System.Threading.Thread.Sleep(500)

                    Call LGOU.GetRange(ENROLL_MODE, lRange)   'same position after 0.5 second ? ---
                    If (lRange = TOO_NEAR_RANGE) Then
                        LGOU.ControlSound(SOU_STEP_BACK)
                        System.Threading.Thread.Sleep(500)   'during user's movement
                    End If
                ElseIf (lRange = TOO_FAR_RANGE) Then 'Too Far Range 
                    If lProgramStatus = CAPTURE_STATE Then InitializeLiveImageBox()
                    System.Threading.Thread.Sleep(500)

                    Call LGOU.GetRange(ENROLL_MODE, lRange)      'same position after 0.5 second ? ---

                    If (lRange = TOO_FAR_RANGE) Then
                        LGOU.ControlSound(SOU_STEP_FORWARD)
                        System.Threading.Thread.Sleep(500)   'during user's movement
                    End If
                ElseIf (lRange = OUT_RANGE) Then 'Out Range 
                    If lProgramStatus = CAPTURE_STATE Then InitializeLiveImageBox()
                End If
            Catch ex As Exception

            End Try

        End Sub
        Private Sub ResetCtrl()
            FocusValue = 0
            InitializeLiveImageBox()
            lProgramStatus = IDLE_STATE
        End Sub
#End Region

#Region "       Download      "
        Protected Overrides Function CreateDownloadCommand(ByVal id As Guid) As AsyncCommand
            Dim DownloadCommand As New AsyncCommand(DownloadCommandTemplate)
            DownloadCommand.TargetMethodArg(0) = id
            Return DownloadCommand
        End Function

        Public Overrides ReadOnly Property DownloadCommandTemplate() As Threading.AsyncCommandTemplate
            Get
                Static download As AsyncCommandTemplate
                If download Is Nothing Then
                    download = New AsyncCommandTemplate
                    With download
                        .TargetMethod = CreateDelegate(GetType(DownloadDelegate), Me, "DownloadImage")
                        Dim args As Object() = {Guid.Empty}
                        .TargetMethodArgs(args)
                        .ExpirationTime = 5000 ' 3mins
                        .IgnoreUnderlyingHandleOfTargetControl = True
                    End With
                End If
                Return download
            End Get
        End Property
        Private Delegate Function DownloadDelegate(ByVal id As Guid) As Object
        Private Function DownloadImage(ByVal id As Guid) As Object
        End Function
#End Region

#Region "      ImageBox Functions      "
        Public Sub InitializeLiveImageBox()
            'VerifyImageBox
            Dim bitmap As New Drawing.Bitmap(Width, Height, Imaging.PixelFormat.Format8bppIndexed)
            bitmap.Palette = EmptyPalette
            IrisImageBox.SizeMode = PictureBoxSizeMode.StretchImage
            IrisImageBox.Image = bitmap
        End Sub

#End Region

#Region "       LG Control varaibles          "

        Private LGOU As New DeviceControl.DeviceControlClass
        Private LGIris As New Iris.Iris
        Private LGTool As New IAToolControl

        '/// Define constant /////////////////////////////////////////////////////
        Private Const IMAGE_SIZE As Integer = 307200
        Private Const lCAPTURE_TIME As Integer = 3000             'millisecond
        Private Const TIME_TO_GET_IRISIMAGE As Integer = 15000    'millisecond
        Private Const TIME_OUT As Integer = 30                    'second
        Private Const TIME_OUT_IDENTIFY As Integer = 6            'second

        Private Const IMAGE_NUMBER As Integer = 32

        Private Const IRISCODE_SIZE As Integer = 512

        Private lProgramStatus As Integer
        'Program status ----------------------
        Private Const IDLE_STATE As Integer = &H0
        Private Const ENROLL_STATE As Integer = &H1
        Private Const RECOGNIZE_STATE As Integer = &H2
        Private Const CAPTURE_STATE As Integer = &H4
        Private Const EXIT_STATE As Integer = &H8
        Private Const CLOSE_STATE As Integer = &H16
        Private Const DONE_STATE As Integer = &H32

        ' Private mGoodRightImageIndex As Integer      'Index on best captured image with the right illuminator : 0~15
        ' Private mRightIrisQuality As Integer
        ' Private mGoodLeftImageIndex As Integer      'Index on best captured image with the left illuminator : 16~31
        ' Private mLeftIrisQuality As Integer
#End Region

        Private Sub CancelSensorButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles CancelSensorButton.Click
            WritablePollingWasCanceled = True
        End Sub

    End Class
End Namespace


