''  ---
''  License
''  ---
''
''  This software was developed at the National Institute of Standards and
''  Technology (NIST) by employees of the Federal Government in the course
''  of their official duties. Pursuant to title 17 Section 105 of the
''  United States Code, this software is not subject to copyright protection
''  and is in the public domain. NIST assumes no responsibility  whatsoever for
''  its use by other parties, and makes no guarantees, expressed or implied,
''  about its quality, reliability, or any other characteristic.
''
''  ---
''  Disclaimer
''  ---
''  This software was developed to facilitate multimodal biometric data collection
''  for the Department of Homeland Security in accordance with Section 303 of the 
''  Border Security Act, codified as 8 U.S.C. 1732.  Specific hardware and software 
''  products identified in this software were used in order to perform the data 
''  collection. In no case does such identification imply recommendation or endorsement 
''  by the National Institute of Standards and Technology, nor does it imply that the 
''  products and equipment identified are necessarily the best available for the purpose.
''

Option Strict On
Imports System.IO
Imports System.Drawing
Imports Mbark.UI

Namespace Mbark.UI

    Public Module ExifReader

        'Private Image As System.Drawing.Bitmap
        Private mImage As Image

        Public Function getExifHeader(ByVal ImageByte() As Byte) As ExifProperties
            Dim s As MemoryStream = New MemoryStream(ImageByte)
            mImage = Image.FromStream(s)
            Return GetExifProperty()
        End Function
        Private Function GetExifProperty() As ExifProperties
            Dim ExifProp As New ExifProperties
            Try
                ExifProp.Aperture = CDbl(GetItemValue(TagNames.ExifFNumber))
                ExifProp.ContrastProcessing = CType(GetItemValue(TagNames.ExifContrastProcessin), Contrast)
                ExifProp.DigitalZRatio = CDbl(GetItemValue(TagNames.ExifDigialZRatio))
                ExifProp.ExposureBias = CDbl(GetItemValue(TagNames.ExifExposureBias))
                ExifProp.ExposureProgram = CShort(GetItemValue(TagNames.ExifExposureProg))
                'ExifProp.ExposureTime = CDbl(GetItemValue(TagNames.ExifExposureTime))
                getFlashmode(ExifProp)
                getFlashstate(ExifProp)
                getFlashRedEye(ExifProp)
                ExifProp.FocalLength = CDbl(GetItemValue(TagNames.ExifFocalLength))
                ExifProp.GainControl = CType(GetItemValue(TagNames.ExifGainControl), GainControl)
                ExifProp.ISO = CShort(GetItemValue(TagNames.ExifISOSpeed))
                ExifProp.IlluminationType = CInt(GetItemValue(TagNames.ExifLightSource))
                ExifProp.MeteringMode = CShort(GetItemValue(TagNames.ExifMeteringMode))
                ExifProp.SaturationProcessing = CType(GetItemValue(TagNames.ExifSaturationProcessing), Saturation)
                ExifProp.SharpnessProcessing = CType(GetItemValue(TagNames.ExifSharpnessProcessiong), Sharpness)
                ExifProp.WhiteBalance = CType(GetItemValue(TagNames.ExifWhiteBalance), WhiteBalance)
                ExifProp.ImageHeight = mImage.Height
                ExifProp.ImageWidth = mImage.Width
                Return ExifProp
            Catch ex As Exception
                Debugger.Break()
            End Try

        End Function
        Private Sub getFlashstate(ByRef ExifProp As ExifProperties)
            Dim P As System.Drawing.Imaging.PropertyItem = mImage.GetPropertyItem(TagNames.ExifFlash)
            Dim value As Integer = GetInt16(P.Value)
            'determine Flash State
            Select Case value
                Case 0, 16, 24
                    ExifProp.FlashState = FlashState.No
                Case 32
                    ExifProp.FlashState = FlashState.Unknown
                Case Else
                    ExifProp.FlashState = FlashState.Yes
            End Select
        End Sub
        Private Sub getFlashmode(ByRef ExifProp As ExifProperties)
            Dim P As System.Drawing.Imaging.PropertyItem = mImage.GetPropertyItem(TagNames.ExifFlash)
            Dim value As Integer = GetInt16(P.Value)
            Select Case value
                Case 9, 13, 15, 16, 73, 77, 79
                    ExifProp.FlashMode = FlashModes.On
                Case 32
                    ExifProp.FlashMode = FlashModes.Off
                Case Else
                    ExifProp.FlashMode = FlashModes.Automatic
            End Select
        End Sub
        '0000.H = Flash did not fire.
        '0001.H = Flash fired.
        '0005.H = Strobe return light not detected.
        '0007.H = Strobe return light detected.
        '0009.H = Flash fired, compulsory flash mode
        '000D.H = Flash fired, compulsory flash mode, return light not detected
        '000F.H = Flash fired, compulsory flash mode, return light detected
        '0010.H = Flash did not fire, compulsory flash mode
        '0018.H = Flash did not fire, auto mode
        '0019.H = Flash fired, auto mode
        '001D.H = Flash fired, auto mode, return light not detected
        '001F.H = Flash fired, auto mode, return light detected
        '0020.H = No flash function
        '0041.H = Flash fired, red-eye reduction mode
        '0045.H = Flash fired, red-eye reduction mode, return light not detected
        '0047.H = Flash fired, red-eye reduction mode, return light detected
        '0049.H = Flash fired, compulsory flash mode, red-eye reduction mode
        '004D.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected
        '004F.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light detected
        '0059.H = Flash fired, auto mode, red-eye reduction mode
        '005D.H = Flash fired, auto mode, return light not detected, red-eye reduction mode
        '005F.H = Flash fired, auto mode, return light detected, red-eye reduction mode
        Private Sub getFlashRedEye(ByRef ExifProp As ExifProperties)
            Dim P As System.Drawing.Imaging.PropertyItem = mImage.GetPropertyItem(TagNames.ExifFlash)
            Dim value As Integer = GetInt16(P.Value)
            'determine RedEye Flash
            Select Case value
                Case 65, 69, 71, 73, 77, 79, 89, 93, 95
                    ExifProp.FlashRedEye = FlashRedEyeMode.On
                Case Else
                    ExifProp.FlashRedEye = FlashRedEyeMode.Off
            End Select
        End Sub
        Private Function GetItemValue(ByVal id As Integer) As Object
            Dim P As System.Drawing.Imaging.PropertyItem = mImage.GetPropertyItem(id)
            Select Case P.Type
                Case 2 'ASCII
                    Return GetString(P.Value)
                Case 3 'Short
                    Return GetInt16(P.Value)
                Case 4, 9 'Long
                    Return GetInt32(P.Value)
                Case 5, 10 'Rational
                    Return GetRational(P.Value).ToDouble()
            End Select
        End Function

        Private Function IsPropertyDefined(ByVal PID As Int32) As Boolean
            Return CBool([Array].IndexOf(mImage.PropertyIdList, PID) > -1)
        End Function

        Public Function GetPropertyRational(ByVal PID As Int32) As Rational
            If IsPropertyDefined(PID) Then
                Return GetRational(mImage.GetPropertyItem(PID).Value)
            Else
                Dim R As Rational
                R.Numerator = 0
                R.Denominator = 1
                Return R
            End If
        End Function

        Public Function GetString(ByVal B As Byte()) As String
            Dim R As String = System.Text.Encoding.ASCII.GetString(B)
            If R.EndsWith(vbNullChar) Then R = R.Substring(0, R.Length - 1)
            Return R
        End Function
        Public Function GetInt16(ByVal B As Byte()) As Int16
            If B.Length < 2 Then Throw New ArgumentException("Data too short (2 bytes expected)", "B")
            Return B(1) << 8 Or B(0)
        End Function
        Public Function GetInt32(ByVal B As Byte()) As Int32
            If B.Length < 4 Then Throw New ArgumentException("Data too short (4 bytes expected)", "B")
            Return B(3) << 24 Or B(2) << 16 Or B(1) << 8 Or B(0)
        End Function
        Public Function GetRational(ByVal B As Byte()) As Rational
            Dim R As New Rational, N(3), D(3) As Byte
            Array.Copy(B, 0, N, 0, 4)
            Array.Copy(B, 4, D, 0, 4)
            R.Denominator = GetInt32(D)
            R.Numerator = GetInt32(N)
            Return R
        End Function

        Public Structure Rational
            Dim Numerator As Int32
            Dim Denominator As Int32

            Shadows Function ToString(Optional ByVal Delimiter As String = "/") As String
                Return Numerator & Delimiter & Denominator
            End Function

            Function ToDouble() As Double
                Dim result As Double = Numerator * 1.0 / Denominator
                Return result
            End Function
        End Structure
    End Module

#Region "       Enumeration     "
    Public Enum TagNames
        ExifAperture = &H9202
        ExifContrastProcessin = &HA408
        ExifDigialZRatio = &HA404
        ExifExposureBias = &H9204
        ExifExposureProg = &H8822
        ExifExposureTime = &H829A
        ExifFileSource = &HA300
        ExifFlash = &H9209
        ExifFNumber = &H829D
        ExifFocalLength = &H920A
        ExifFocalXRes = &HA20E
        ExifFocalYRes = &HA20F
        ExifFocalResUnit = &HA210
        ExifGainControl = &HA407
        ExifISOSpeed = &H8827
        ExifLightSource = &H9208
        ExifMakerNote = &H927C
        ExifMaxAperture = &H9205
        ExifMeteringMode = &H9207
        ExifSaturationProcessing = &HA409
        ExifSceneType = &HA301
        ExifSensingMethod = &HA217
        ExifSharpnessProcessiong = &HA40A
        ExifShutterSpeed = &H9201
        ExifSubjectDist = &H9206
        ExifSubjectLoc = &HA214
        ExifUserComment = &H9286
        ExifWhiteBalance = &HA403
    End Enum
#End Region


End Namespace