Imports System.IO.Ports
Imports System.Runtime.InteropServices
Imports System.Threading

Public Class MainForm

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure BLUEBOX_Tag
        Public TagType As Integer
        Public Id As IntPtr
        Public Length As Integer
        Public Antenna As Integer
        Public Input As Integer
    End Structure

    ''' <summary>
    ''' Write a parameter (srting) in an INI file.
    ''' </summary>
    ''' <param name="section">The section where to write the parameter.</param>
    ''' <param name="key">The key name of the parameter.</param>
    ''' <param name="value">The value of the parameter.</param>
    ''' <param name="filename">The INI file name.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" _
    (ByVal section As String, ByVal key As String, ByVal value As String, ByVal filename As String) As Long

    ''' <summary>
    ''' Read a parameter (string) from a INI file.
    ''' </summary>
    ''' <param name="section">The section where to read the parameter.</param>
    ''' <param name="key">The key name of the parameter.</param>
    ''' <param name="def">The default value of the parameter.</param>
    ''' <param name="value">The value of the parameter.</param>
    ''' <param name="size"></param>
    ''' <param name="filename">The INI file name.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" _
    (ByVal section As String, ByVal key As String, ByVal def As String, ByVal value As System.Text.StringBuilder, ByVal size As Long, ByVal filename As String) As Long

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks></remarks>
    Private BLUEBOXLib As BLUEBOXLibInterface

    ''' <summary>
    ''' Handle that identifies the reader using the bluebox library.
    ''' </summary>
    ''' <remarks></remarks>
    Private BlueboxHandle As Integer

    ''' <summary>
    ''' Thread run flag.
    ''' </summary>
    ''' <remarks></remarks>
    Private ThreadRun As Boolean

    ''' <summary>
    ''' Polling thread variable.
    ''' </summary>
    ''' <remarks></remarks>
    Private t As Thread

    ''' <summary>
    ''' Delegate to avoid cross thread exception error in communication thread.
    ''' </summary>
    ''' <remarks></remarks>
    Delegate Sub ThreadCallBack_0p()

    ''' <summary>
    ''' Delegate to avoid cross thread exception error in communication thread.
    ''' </summary>
    ''' <param name="Obj"></param>
    ''' <remarks></remarks>
    Delegate Sub ThreadCallBack_1p(ByVal Obj As Object)

    ''' <summary>
    ''' Exit toolstrip menu item click event. Closes the application.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click

        System.Windows.Forms.Application.Exit()

    End Sub

    ''' <summary>
    ''' About toolstrip menu item click event. Opens the about form and shows it.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub AboutToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AboutToolStripMenuItem.Click

        Dim AboutBox As New AboutForm()
        AboutBox.ShowDialog()

    End Sub

    ''' <summary>
    ''' Form load event. Configure and initialize the window.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' Set the english culture as current culture.
        Thread.CurrentThread.CurrentCulture = New System.Globalization.CultureInfo("en-GB")
        Thread.CurrentThread.CurrentUICulture = New System.Globalization.CultureInfo("en-GB")

        ' Check the target platform
        If (IntPtr.Size = 8) Then
            BLUEBOXLib = New BLUEBOXLibClass_x64
        ElseIf (IntPtr.Size = 4) Then
            BLUEBOXLib = New BLUEBOXLibClass_x86
        End If

        Try
            ' Create the port name combo box
            Dim Ports As String() = SerialPort.GetPortNames()
            Dim Port As String

            Me.PortNameComboBox.Items.Clear()

            For Each Port In Ports
                Me.PortNameComboBox.Items.Add(Port)
            Next Port

            ' Try to select the first port
            If Me.PortNameComboBox.Items.Count > 0 Then
                Me.PortNameComboBox.SelectedIndex = 0
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

        Try
            Dim Str As New System.Text.StringBuilder(255)
            GetPrivateProfileString("General", "Address", "255", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            Me.AddressTextBox.Text = Str.ToString()
            ' RS232 configuration            
            GetPrivateProfileString("RS232", "Port Name", "COM1", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            If Me.PortNameComboBox.Items.Contains(Str.ToString()) = True Then
                Me.PortNameComboBox.SelectedIndex = Me.PortNameComboBox.Items.IndexOf(Str.ToString())
            End If

            GetPrivateProfileString("RS232", "Baud Rate", "19200", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            If Me.BaudRateComboBox.Items.Contains(Str.ToString()) Then
                Me.BaudRateComboBox.SelectedIndex = Me.BaudRateComboBox.Items.IndexOf(Str.ToString())
            Else
                Me.BaudRateComboBox.SelectedIndex = Me.BaudRateComboBox.Items.IndexOf("19200")
            End If

            GetPrivateProfileString("RS232", "Data Bits", "8", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            If Me.DataBitsComboBox.Items.Contains(Str.ToString()) Then
                Me.DataBitsComboBox.SelectedIndex = Me.DataBitsComboBox.Items.IndexOf(Str.ToString())
            Else
                Me.DataBitsComboBox.SelectedIndex = Me.DataBitsComboBox.Items.IndexOf("8")
            End If

            GetPrivateProfileString("RS232", "Stop Bits", "1", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            If Me.StopBitsComboBox.Items.Contains(Str.ToString()) Then
                Me.StopBitsComboBox.SelectedIndex = Me.StopBitsComboBox.Items.IndexOf(Str.ToString())
            Else
                Me.StopBitsComboBox.SelectedIndex = Me.StopBitsComboBox.Items.IndexOf("1")
            End If

            GetPrivateProfileString("RS232", "Parity", "None", Str, 255, Application.StartupPath + "\\BLUEBOX Test.ini")
            If Me.ParityComboBox.Items.Contains(Str.ToString()) Then
                Me.ParityComboBox.SelectedIndex = Me.ParityComboBox.Items.IndexOf(Str.ToString())
            Else
                Me.ParityComboBox.SelectedIndex = Me.ParityComboBox.Items.IndexOf("None")
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

    End Sub

    ''' <summary>
    ''' Port name combo box click event. Generate the port name combo box.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub PortNameComboBox_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PortNameComboBox.Click

        Try
            ' Create the port name combo box
            Dim Ports As String() = SerialPort.GetPortNames()
            Dim Port As String

            Me.PortNameComboBox.Items.Clear()

            For Each Port In Ports
                Me.PortNameComboBox.Items.Add(Port)
            Next Port

            ' Try to select the first port
            If Me.PortNameComboBox.Items.Count > 0 Then
                Me.PortNameComboBox.SelectedIndex = 0
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

    End Sub

    ''' <summary>
    ''' Start button click event. Manage connection/disconnection.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub StartButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StartButton.Click

        If Me.StartButton.Text = "Start" Then

            Dim Address As Byte
            Dim Settings As String
            Dim Err As Integer

            ' Check the port names
            If Me.PortNameComboBox.Items.Count <= 0 Then
                MessageBox.Show("No Com ports available!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Stop)
                Return
            End If

            ' Convert the address
            Try
                Address = Convert.ToByte(Me.AddressTextBox.Text)
            Catch ex As Exception
                MessageBox.Show("Type a valid device address (0 - 255)!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Stop)
                Return
            End Try

            ' Generate the settings string
            Settings = System.String.Format("{0},{1},{2},{3},{4},5,60000", Me.PortNameComboBox.SelectedItem.ToString(), Me.BaudRateComboBox.SelectedItem.ToString(), Me.DataBitsComboBox.SelectedItem.ToString(), Me.ParityComboBox.SelectedItem.ToString().Substring(0, 1).ToLower(), Me.StopBitsComboBox.SelectedItem.ToString())

            ' Initialize the library
            If BLUEBOXLib._Init(BlueboxHandle) <> 0 Then
                MessageBox.Show("BLUEBOXLib.dll: Library load error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return
            End If

            ' Sets the address
            Err = BLUEBOXLib._SetAddress(BlueboxHandle, Address)
            If Err <> 0 Then
                MessageBox.Show("BLUEBOXLib.dll: Library initialization error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
            End If

            ' Sets the channel
            If Err = 0 Then
                Err = BLUEBOXLib._SetChannel(BlueboxHandle, "RS232", Settings)
                If Err <> 0 Then
                    MessageBox.Show("BLUEBOXLib.dll: Library initialization error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End If

            If Err <> 0 Then
                BLUEBOXLib._End(BlueboxHandle)
                Return
            End If

            ' open the connection
            If Err = 0 Then
                Err = BLUEBOXLib._Open(BlueboxHandle)
                If Err <> 0 Then
                    MessageBox.Show("BLUEBOXLib.dll: Connection error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End If

            If Err <> 0 Then
                BLUEBOXLib._Close(BlueboxHandle)
                BLUEBOXLib._End(BlueboxHandle)
                Return
            End If

            ' Read the fw version
            If Err = 0 Then
                Dim FwRel As New System.Text.StringBuilder("")
                Err = BLUEBOXLib._GetFwRelease(BlueboxHandle, 0, FwRel)

                If Err <> 0 Then
                    MessageBox.Show("BLUEBOXLib.dll: Communication error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End If

            If Err <> 0 Then
                BLUEBOXLib._Close(BlueboxHandle)
                BLUEBOXLib._End(BlueboxHandle)
            End If

            ' Read the general parameters
            If Err = 0 Then
                Dim Params As Byte() = New Byte(7) {}
                Err = BLUEBOXLib._ReadParameters(BlueboxHandle, Params)

                If Err <> 0 Then
                    MessageBox.Show("BLUEBOXLib.dll: Communication error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            End If

            If Err <> 0 Then
                BLUEBOXLib._Close(BlueboxHandle)
                BLUEBOXLib._End(BlueboxHandle)
            End If

            Try

                If Err = 0 Then
                    ThreadRun = True

                    t = New Thread(AddressOf PollingThread)
                    ' Set the english culture as current culture.
                    t.CurrentCulture = New System.Globalization.CultureInfo("en-GB")
                    t.CurrentUICulture = New System.Globalization.CultureInfo("en-GB")
                    ' Start the thread.
                    t.Start()

                    OpeningConnection()
                End If

            Catch ex As Exception
                MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try

        Else

            ThreadRun = False

        End If

    End Sub

    ''' <summary>
    ''' This function is the thread that manages the reader polling. It reads the tags and print IDs in the table.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PollingThread()

        While (ThreadRun)

            Dim TagsNo As Integer
            Dim Tags As IntPtr = IntPtr.Zero
            Dim Err As Integer

            Try
                ' Data request
                Err = BLUEBOXLib._DataRequest(BlueboxHandle, Tags, TagsNo)

                If Err = 0 Then
                    ' Print the tags read
                    Dim Index As Integer

                    For Index = 0 To TagsNo - 1 Step 1
                        ' Pointer to the buffer that contains data received from the call of data request
                        Dim Tmp As IntPtr = CType(Tags, Integer) + (Index * Marshal.SizeOf(GetType(BLUEBOX_Tag)))
                        ' Now got the pointer to the struct, get the structure from the memory
                        Dim Tag As BLUEBOX_Tag = CType(Marshal.PtrToStructure(Tmp, GetType(BLUEBOX_Tag)), BLUEBOX_Tag)
                        ' Create the string array to print in data grid view
                        Dim Antenna As String = Tag.Antenna.ToString()
                        Dim Input As String = Tag.Input.ToString()

                        Dim TagType As New String("-")

                        Select Case (Tag.TagType)
                            Case 1
                                TagType = "BLUEBOX SHORT"
                            Case 2
                                TagType = "BLUEBOX MEDIUM"
                            Case 3
                                TagType = "BLUEBOX LARGE"
                            Case 8
                                TagType = "ISO 14443A"
                            Case 9
                                TagType = "NXP MIFARE 1k"
                            Case 10
                                TagType = "NXP MIFARE 4k"
                            Case 11
                                TagType = "NXP MIFARE Ultralight"
                            Case 12
                                TagType = "ISO 15693"
                            Case 13
                                TagType = "NXP ICODE SLI"
                            Case 14
                                TagType = "TI Tag-It HF-I"
                            Case 15
                                TagType = "EM EM4035"
                            Case 16
                                TagType = "ST LRI 64/512"
                            Case 17
                                TagType = "FUJITSU MB89R118"
                            Case 18
                                TagType = "ISO 14443B"
                            Case 19
                                TagType = "ST SR 176"
                            Case 20
                                TagType = "ISO 18000-6B"
                            Case 21
                                TagType = "ISO 18000-6C"
                            Case 22
                                TagType = "ACTIVE"
                            Case 25
                                TagType = "NXP ICODE SLI-S"
                            Case 26
                                TagType = "NXP HITAG 1"
                            Case 27
                                TagType = "NXP MIFARE Mini"
                            Case 28
                                TagType = "NXP MIFARE Desfire"
                            Case 29
                                TagType = "NXP MIFARE 1k 7-byte-UID"
                            Case 30
                                TagType = "NXP MIFARE 4k 7-byte-UID"
                            Case 31
                                TagType = "NXP MIFARE Plus 2k"
                            Case 32
                                TagType = "NXP MIFARE Plus 4k"
                            Case 33
                                TagType = "ST SRI 512"
                            Case 34
                                TagType = "JCos"
                            Case 35
                                TagType = "PICOPASS"
                            Case 37
                                TagType = "IBM JCOP"
                            Case 38
                                TagType = "IBM JCOP31"
                            Case 39
                                TagType = "IBM JCOP41"
                        End Select

                        Dim Id As New String("")
                        Dim k As Integer

                        For k = 0 To Tag.Length - 1 Step 1
                            Id &= Marshal.ReadByte(Tag.Id, k).ToString("X2").ToUpper() & " "
                        Next
                        Id = Id.Remove(Id.Length - 1, 1)

                        ' Create the row to add to the table
                        Dim Row(4) As String
                        Row(0) = Antenna
                        Row(1) = Input
                        Row(2) = TagType
                        Row(3) = Id
                        Row(4) = "1"

                        Me.Invoke(New ThreadCallBack_1p(AddressOf AddRowToTagDataGridView), CType(Row, Object))
                    Next

                    BLUEBOXLib._FreeTagsMemory(BlueboxHandle, Tags, TagsNo)
                End If

                Select Case (Err)
                    Case -2
                        MessageBox.Show("BLUEBOXLib.dll: Invalid handle!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -4
                        MessageBox.Show("BLUEBOXLib.dll: Invalid parameters!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -5
                        MessageBox.Show("BLUEBOXLib.dll: Generic error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -6
                        MessageBox.Show("BLUEBOXLib.dll: Timeout error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -7
                        MessageBox.Show("BLUEBOXLib.dll: Communication error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -8
                        MessageBox.Show("BLUEBOXLib.dll: Connection error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -9
                        MessageBox.Show("BLUEBOXLib.dll: Memory allocation error!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -10
                        MessageBox.Show("BLUEBOXLib.dll: Invalid command!", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
                    Case -11
                        TagsNo = 0
                        Err = 0
                    Case -12
                        Err = 0
                End Select

                If Err <> 0 Then
                    ThreadRun = False
                End If

            Catch ex As Exception
                MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try

            Thread.Sleep(10)

        End While

        Try
            ' Close the connection
            BLUEBOXLib._Close(BlueboxHandle)
            ' Free resources
            BLUEBOXLib._End(BlueboxHandle)
        Catch ex As Exception
            MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            Me.Invoke(New ThreadCallBack_0p(AddressOf ClosingConnection))
        End Try

    End Sub

    ''' <summary>
    ''' Adds a row to the data grid view table.
    ''' </summary>
    ''' <param name="Row">Row to add to the data grid view table.</param>
    ''' <remarks></remarks>
    Private Sub AddRowToTagDataGridView(ByVal Row As Object)

        Dim RowFound As Boolean = False
        Dim RowIndex As Integer

        ' Search the row in data table
        For RowIndex = 0 To Me.TagDataGridView.RowCount - 1 Step 1
            If Me.TagDataGridView(Me.AntennaColumn.Index, RowIndex).Value.ToString() = CType(Row, String())(0) AndAlso Me.TagDataGridView(Me.InputColumn.Index, RowIndex).Value.ToString() = CType(Row, String())(1) AndAlso Me.TagDataGridView(Me.TagTypeColumn.Index, RowIndex).Value.ToString() = CType(Row, String())(2) AndAlso Me.TagDataGridView(Me.IdColumn.Index, RowIndex).Value.ToString() = CType(Row, String())(3) Then
                ' Row found
                RowFound = True
                ' Update counter
                Dim Count As Integer = Convert.ToInt32(Me.TagDataGridView(Me.CountColumn.Index, RowIndex).Value)
                Me.TagDataGridView(Me.CountColumn.Index, RowIndex).Value = Count + 1
            End If
        Next

        If RowFound = False Then
            Me.TagDataGridView.Rows.Add(CType(Row, String()))
        End If

    End Sub

    ''' <summary>
    ''' Manage controls enable status on opening connection event.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub OpeningConnection()

        Me.StartButton.Text = "Stop"
        Me.AddressGroupBox.Enabled = False
        Me.RS232GroupBox.Enabled = False

        Me.TagDataGridView.Rows.Clear()

    End Sub

    ''' <summary>
    ''' Manage controls enable status on closing connection event.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ClosingConnection()

        Me.StartButton.Text = "Start"
        Me.AddressGroupBox.Enabled = True
        Me.RS232GroupBox.Enabled = True

    End Sub

    ''' <summary>
    ''' Form closing event. Save configuration in an INI file.
    ''' </summary>
    ''' <param name="sender">Object that raised the event.</param>
    ''' <param name="e">Arguments of the event.</param>
    ''' <remarks></remarks>
    Private Sub MainForm_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing

        If Me.AddressGroupBox.Enabled = False Then
            MessageBox.Show("Acquisition is running." & Chr(13) & Chr(13) & "Stop the acquisition before closing the application", Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Warning)
            e.Cancel = True
        Else
            Try
                WritePrivateProfileString("General", "Address", AddressTextBox.Text, Application.StartupPath + "\\BLUEBOX Test.ini")
                ' Write RS232 configuration
                If Me.PortNameComboBox.SelectedIndex >= 0 Then
                    WritePrivateProfileString("RS232", "Port Name", Me.PortNameComboBox.SelectedItem.ToString(), Application.StartupPath + "\\BLUEBOX Test.ini")
                Else
                    WritePrivateProfileString("RS232", "Port Name", "", Application.StartupPath + "\\BLUEBOX Test.ini")
                End If

                WritePrivateProfileString("RS232", "Baud Rate", Convert.ToInt32(Me.BaudRateComboBox.SelectedItem.ToString()), Application.StartupPath + "\\BLUEBOX Test.ini")
                WritePrivateProfileString("RS232", "Data Bits", Convert.ToInt32(Me.DataBitsComboBox.SelectedItem.ToString()), Application.StartupPath + "\\BLUEBOX Test.ini")
                WritePrivateProfileString("RS232", "Stop Bits", Convert.ToInt32(Me.StopBitsComboBox.SelectedItem.ToString()), Application.StartupPath + "\\BLUEBOX Test.ini")
                WritePrivateProfileString("RS232", "Parity", Me.ParityComboBox.SelectedItem.ToString(), Application.StartupPath + "\\BLUEBOX Test.ini")
            Catch ex As Exception
                MessageBox.Show(ex.Message & Chr(13) & Chr(13) & ex.ToString(), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End If

    End Sub
End Class
