''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright (c) Datacard Corporation.  All Rights Reserved.
'
' XPS Printer Driver SDK: Single-wire smartcard personalization vb.net sample.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Imports System.IO
Imports dxp01sdk
Imports SmartcardCommonNamespace

Public Class CommandLineOptions
    Public printerName As String
    Public print As Boolean
    Public jobCompletion As Boolean
    Public parkBack As Boolean
    Public hopperID As String = ""
    Public cardEjectSide As String = ""

    Public Sub Validate()
        If hopperID <> "" Then

            If hopperID <> "1" AndAlso hopperID <> "2" AndAlso hopperID <> "3" AndAlso hopperID <> "4" AndAlso hopperID <> "5" AndAlso hopperID <> "6" AndAlso hopperID <> "exception" Then
                Console.WriteLine("invalid hopperID: {0}", hopperID)
                Environment.[Exit](-1)
            End If
        End If

        If cardEjectSide <> "" Then

            If cardEjectSide <> "front" AndAlso cardEjectSide <> "back" Then
                Console.WriteLine("invalid cardEjectSide: {0}", cardEjectSide)
                Environment.[Exit](-1)
            End If
        End If
    End Sub
End Class

Class smartcard_singlewire
    Enum CardType
        contact
        contactless
        undetermined
    End Enum

    ' Escapes are top to area; left to area; width; height : card in portrait orientation
    Private Shared printBlockingEscape As String = "~PB%7.26 22.35 14.99 14.99?"
    Private Shared topcoatRemovalEscape As String = "~TR%7.26 22.35 14.99 14.99?"

    Private Shared Sub Usage()
        Dim thisExeName As String = Path.GetFileName(Environment.GetCommandLineArgs()(0))
        Console.Write(thisExeName)
        Console.WriteLine(" demonstrates interactive mode parking a card in the")
        Console.WriteLine("  smart card station, performing single-wire smartcard functions, moving")
        Console.WriteLine("  the card from the station, and options to print and poll for job completion.")
        Console.WriteLine()
        Console.WriteLine("Uses hardcoded data for printing.")
        Console.WriteLine()
        Console.WriteLine(thisExeName & " -n <printername> [-p] [-b] [-c] [-i <input hopper>] [-f <Front | Back>]")
        Console.WriteLine()
        Console.WriteLine("options:")
        Console.WriteLine("  -n <printername>. Required. Try -n ""XPS Card Printer"".")
        Console.WriteLine("  -p Print text and graphic on front side of card.")
        Console.WriteLine("  -b use back of card for smartcard chip.")
        Console.WriteLine("  -c Poll for job completion.")
        Console.WriteLine("  -f <Front | Back> Specify the front or back side the card will eject from.")
        Console.WriteLine("  -i <input hopper> Defaults to input hopper #1.")
        Console.WriteLine()
        Console.WriteLine(thisExeName & " -n ""XPS Card Printer"" -p -c")
        Console.WriteLine("  Parks a card in printer smart card station, moves the card from the station,")
        Console.WriteLine("  prints simple text and graphics on the front side of the card, and polls and")
        Console.WriteLine("  displays job status.")
        Environment.Exit(-1)
    End Sub

    Private Shared Function GetCommandlineOptions(args As String()) As CommandLineOptions

        If args.Length = 0 Then
            Usage()
        End If

        Dim commandLineOptions As CommandLineOptions = New CommandLineOptions()
        Dim arguments As CommandLine.Utility.Arguments = New CommandLine.Utility.Arguments(args)

        If String.IsNullOrEmpty(arguments("n")) Then
            Console.WriteLine("printer name is required")
            Environment.Exit(-1)
        Else
            ' we might have a -n with no printer name:
            Dim boolVal As Boolean = False
            If Boolean.TryParse(arguments("n"), boolVal) Then
                Console.WriteLine("printer name is required")
                Environment.Exit(-1)
            End If
            commandLineOptions.printerName = arguments("n")
        End If

        commandLineOptions.print = Not String.IsNullOrEmpty(arguments("p"))
        commandLineOptions.parkBack = Not String.IsNullOrEmpty(arguments("b"))
        commandLineOptions.jobCompletion = Not String.IsNullOrEmpty(arguments("c"))
        commandLineOptions.hopperID = If(String.IsNullOrEmpty(arguments("i")), String.Empty, arguments("i").ToLower())
        commandLineOptions.cardEjectSide = If(String.IsNullOrEmpty(arguments("f")), String.Empty, arguments("f").ToLower())

        Return commandLineOptions
    End Function



    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ''' <summary>
    ''' a card is in the printer and stated in the smartcard module. The
    ''' SCardConnect() call has succeeded.
    ''' talk to the reader and the chip (contact or contactless).
    ''' </summary>
    ''' <param name="scard">an instance of our SCard wrapper class.</param>
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Shared Function PersonalizeConnectedChip(scard__1 As SCard) As Boolean

        Console.WriteLine()
        SmartcardCommon.GetSCardStatus(scard__1)
        Console.WriteLine()
        SmartcardCommon.ReadVendorInfo(scard__1)


        ' create a bytes for the upcoming SCardTransmit() method.
        ' these particular bytes should function with this type of contact chip:
        '
        '      MPCOS-EMV 16k
        '      GEMPLUS
        '      Datacard part number 806062-002
        '
        Dim sendBytes As Byte() = New Byte() {&H0, &HA4, &H0, &H0}
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex("sending bytes : ", sendBytes)

        ' for those bytes, we should receive 0x61, 0x12
        Dim receivedBytes As Byte() = New Byte(-1) {}

        Dim scardResult As Long = scard__1.SCardTransmit(sendBytes, receivedBytes)

        Console.WriteLine("SCardTransmit result: {0}", scardResult)

        If CLng(scard_error.SCARD_S_SUCCESS) = scardResult AndAlso receivedBytes.Length > 0 Then
            SmartcardCommon.DisplayByteVectorAsHex("bytes received: ", receivedBytes)
        End If

        ' send the bytes       0x00  0xC0  0x00  0x00  0x12:
        sendBytes = New Byte() {&H0, &HC0, &H0, &H0, &H12}
        Console.WriteLine()
        SmartcardCommon.DisplayByteVectorAsHex("sending bytes : ", sendBytes)

        ' for those bytes, we should receive
        '  0x85, 0x10, 0x80, 0x01, 0x3F, 0x00, 0x38, 0x00, 0x00, 0x00,
        '  0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x90, 0x00
        receivedBytes = New Byte(-1) {}

        scardResult = scard__1.SCardTransmit(sendBytes, receivedBytes)

        Console.WriteLine("SCardTransmit result: {0}", scardResult)

        If CLng(scard_error.SCARD_S_SUCCESS) = scardResult AndAlso receivedBytes.Length > 0 Then
            SmartcardCommon.DisplayByteVectorAsHex("bytes received: ", receivedBytes)
        End If
        Return True
    End Function

    ''' <summary>
    ''' Demonstrate the single-wire smartcard calls. The wrapper class we consume
    ''' is similar to the Win32 ::Scard...() functions.
    ''' </summary>
    Private Shared Function PersonalizeSmartcard(bidiSpl As BidiSplWrap) As CardType

        Dim scard As SCard = New SCard(bidiSpl)
        Dim contactPersonalized As Boolean = False
        Dim contactlessPersonalized As Boolean = False
        Dim cardType As CardType = cardType.undetermined

        ' try a contacted chip:
        Dim protocol As UInteger = CUInt(scard_protocol.SCARD_PROTOCOL_DEFAULT)

        Dim scardResult As Long = scard.SCardConnect(scard.ChipConnection.contact, protocol)
        Console.WriteLine("SCardConnect(contact): {0}: {1}", scardResult, Util.Win32ErrorString(CUInt(scardResult)))

        If CLng(scard_error.SCARD_S_SUCCESS) = scardResult Then
            cardType = cardType.contact
            Dim protocols As String() = SCard.StringsFromProtocol(protocol)

            Console.WriteLine()
            SmartcardCommon.DisplayProtocols(protocol)

            contactPersonalized = PersonalizeConnectedChip(scard)

            scardResult = scard.SCardDisConnect(CInt(scard_disposition.SCARD_LEAVE_CARD))

            Console.WriteLine()
            Console.WriteLine("SCardDisConnect() result: {0}", scardResult)
        End If

        ' try a contactless chip:
        Console.WriteLine()
        protocol = CUInt(scard_protocol.SCARD_PROTOCOL_DEFAULT)

        scardResult = scard.SCardConnect(scard.ChipConnection.contactless, protocol)
        Console.WriteLine("SCardConnect(contactless): {0}: {1}", scardResult, Util.Win32ErrorString(CUInt(scardResult)))

        If CLng(scard_error.SCARD_S_SUCCESS) = scardResult Then
            cardType = cardType.contactless
            Dim protocols As String() = scard.StringsFromProtocol(protocol)

            Console.WriteLine()
            SmartcardCommon.DisplayProtocols(protocol)

            contactlessPersonalized = PersonalizeConnectedChip(scard)

            scardResult = scard.SCardDisConnect(CInt(scard_disposition.SCARD_LEAVE_CARD))

            Console.WriteLine()
            Console.WriteLine("SCardDisConnect() result: {0}", scardResult)
        End If

        If Not contactPersonalized AndAlso Not contactlessPersonalized Then
            Throw New Exception("neither contact nor contactless chip personalization succeeded. canceling job.")
        End If
        Return cardType
    End Function


    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' Main()
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Public Shared Sub Main(args As String())

        Dim commandLineOptions As CommandLineOptions = GetCommandlineOptions(args)
        commandLineOptions.Validate()

        Dim bidiSpl As BidiSplWrap = Nothing
        Dim printerJobID As Integer = 0

        Try
            bidiSpl = New BidiSplWrap()
            bidiSpl.BindDevice(commandLineOptions.printerName)

            ' optional:
            Dim driverVersionXml As String = bidiSpl.GetPrinterData(strings.SDK_VERSION)
            Console.WriteLine(Environment.NewLine & "driver version: " & Util.ParseDriverVersionXML(driverVersionXml) & Environment.NewLine)

            ' see if the printer is in the Printer_Ready state:
            Dim printerOptionsXml As String = bidiSpl.GetPrinterData(strings.PRINTER_OPTIONS2)

            Dim printerOptionsValues As PrinterOptionsValues = Util.ParsePrinterOptionsXML(printerOptionsXml)
            If "Ready" <> printerOptionsValues._printerStatus AndAlso "Busy" <> printerOptionsValues._printerStatus Then
                Throw New Exception((commandLineOptions.printerName & " is not ready. status: ") + printerOptionsValues._printerStatus)
            End If

            If "Single wire" <> printerOptionsValues._optionSmartcard Then
                Dim msg As String = String.Format("{0} needs 'Single wire' for smartcard option. '{1}' was returned.",
                    commandLineOptions.printerName, printerOptionsValues._optionSmartcard)
                Throw New Exception(msg)
            End If

            If commandLineOptions.print AndAlso "Installed" <> printerOptionsValues._printHead AndAlso "Version1" <> printerOptionsValues._connectionProtocol Then
				Throw New Exception(commandLineOptions.printerName & " does not have a print head installed.")
            End If

            Dim hopperID As String = Util.GetInputHopper(printerOptionsValues._optionInputHopper, commandLineOptions.hopperID)
            Dim cardEjectSide As String = "default"

            printerJobID = Util.StartJob(bidiSpl,
                                         hopperID,
                                         If((commandLineOptions.cardEjectSide.Length > 0), commandLineOptions.cardEjectSide, cardEjectSide))

            SmartcardCommon.ParkCard(bidiSpl, commandLineOptions.parkBack)

            Dim cardType As CardType = PersonalizeSmartcard(bidiSpl)

            SmartcardCommon.ResumeCard(bidiSpl, printerJobID, 0)

            If commandLineOptions.print Then
                If cardType = cardType.contact AndAlso commandLineOptions.parkBack <> True Then
                    Util.PrintTextAndGraphics(bidiSpl, commandLineOptions.printerName, printBlockingEscape, topcoatRemovalEscape)
                Else
                    Util.PrintTextAndGraphics(bidiSpl, commandLineOptions.printerName)
                End If
                Util.WaitForWindowsJobID(bidiSpl, commandLineOptions.printerName)
            End If

            bidiSpl.SetPrinterData(strings.ENDJOB)

            If commandLineOptions.jobCompletion Then
                Util.PollForJobCompletion(bidiSpl, printerJobID)
            End If
        Catch e As BidiException
            Console.WriteLine(e.Message)
            Util.CancelJob(bidiSpl, e.PrinterJobID, e.ErrorCode)
        Catch e As Exception
            Console.WriteLine(e.Message)
            If 0 <> printerJobID Then
                Util.CancelJob(bidiSpl, printerJobID, 0)
            End If
        Finally
            bidiSpl.UnbindDevice()
        End Try
    End Sub
End Class