Imports System
Imports System.Collections.Generic
Imports System.IO
Imports SelectPdf

Namespace SelectPdf.Samples.CommandLine

    ''' <summary>
    ''' Command line sample that converts an URL or HTML file/string to PDF using SelectPdf,
    ''' exposing the major HtmlToPdf settings as command line arguments.
    ''' Targets net8.0 and net10.0.
    ''' </summary>
    Friend Module Program

        Public Function Main(ByVal args As String()) As Integer
            If args.Length = 0 OrElse HasFlag(args, "--help") OrElse HasFlag(args, "-h") OrElse HasFlag(args, "/?") Then
                PrintUsage()
                Return 0
            End If

            Dim cli As CliArgs
            Try
                cli = CliArgs.Parse(args)
            Catch ex As Exception
                Console.Error.WriteLine("Argument error: " & ex.Message)
                Console.Error.WriteLine("Run with --help to see the list of options.")
                Return 2
            End Try

            Dim inputCount As Integer = 0
            If Not String.IsNullOrEmpty(cli.Url) Then inputCount += 1
            If Not String.IsNullOrEmpty(cli.HtmlFile) Then inputCount += 1
            If Not String.IsNullOrEmpty(cli.HtmlString) Then inputCount += 1
            If inputCount <> 1 Then
                Console.Error.WriteLine("You must specify exactly one of: --url, --html-file, --html")
                Return 2
            End If

            If String.IsNullOrEmpty(cli.Output) Then
                cli.Output = "output.pdf"
            End If

            Try
                ConvertToPdf(cli)
                Console.WriteLine("PDF created: " & Path.GetFullPath(cli.Output))
                Return 0
            Catch ex As Exception
                Console.Error.WriteLine("Conversion failed: " & ex.Message)
                Console.Error.WriteLine(ex.ToString())
                Return 1
            End Try
        End Function

        Private Sub ConvertToPdf(ByVal cli As CliArgs)
            If Not String.IsNullOrEmpty(cli.LicenseKey) Then
                GlobalProperties.LicenseKey = cli.LicenseKey
            End If

            Dim converter As New HtmlToPdf()
            Dim opt As HtmlToPdfOptions = converter.Options

            ' rendering engine
            opt.RenderingEngine = ParseEnum(Of RenderingEngine)(cli.Engine, RenderingEngine.WebKit)

            ' page size, orientation, margins
            opt.PdfPageSize = ParseEnum(Of PdfPageSize)(cli.PageSize, PdfPageSize.A4)
            opt.PdfPageOrientation = ParseEnum(Of PdfPageOrientation)(cli.Orientation, PdfPageOrientation.Portrait)
            If cli.MarginLeft.HasValue Then opt.MarginLeft = cli.MarginLeft.Value
            If cli.MarginRight.HasValue Then opt.MarginRight = cli.MarginRight.Value
            If cli.MarginTop.HasValue Then opt.MarginTop = cli.MarginTop.Value
            If cli.MarginBottom.HasValue Then opt.MarginBottom = cli.MarginBottom.Value

            ' browser viewport
            If cli.WebPageWidth.HasValue Then opt.WebPageWidth = cli.WebPageWidth.Value
            If cli.WebPageHeight.HasValue Then opt.WebPageHeight = cli.WebPageHeight.Value
            If cli.WebPageFixedSize.HasValue Then opt.WebPageFixedSize = cli.WebPageFixedSize.Value

            ' auto fit
            If Not String.IsNullOrEmpty(cli.AutoFitWidth) Then _
                opt.AutoFitWidth = ParseEnum(Of HtmlToPdfPageFitMode)(cli.AutoFitWidth, opt.AutoFitWidth)
            If Not String.IsNullOrEmpty(cli.AutoFitHeight) Then _
                opt.AutoFitHeight = ParseEnum(Of HtmlToPdfPageFitMode)(cli.AutoFitHeight, opt.AutoFitHeight)

            ' timing
            If cli.MinPageLoadTime.HasValue Then opt.MinPageLoadTime = cli.MinPageLoadTime.Value
            If cli.MaxPageLoadTime.HasValue Then opt.MaxPageLoadTime = cli.MaxPageLoadTime.Value
            If cli.MaxConversionTime.HasValue Then opt.MaxConversionTime = cli.MaxConversionTime.Value

            ' compression
            If cli.JpegCompression.HasValue Then opt.JpegCompressionEnabled = cli.JpegCompression.Value
            If cli.JpegCompressionLevel.HasValue Then opt.JpegCompressionLevel = cli.JpegCompressionLevel.Value
            If Not String.IsNullOrEmpty(cli.CompressionLevel) Then _
                opt.PdfCompressionLevel = ParseEnum(Of PdfCompressionLevel)(cli.CompressionLevel, opt.PdfCompressionLevel)

            ' links
            If cli.InternalLinks.HasValue Then opt.InternalLinksEnabled = cli.InternalLinks.Value
            If cli.ExternalLinks.HasValue Then opt.ExternalLinksEnabled = cli.ExternalLinks.Value

            ' scripting and rendering behavior
            If cli.JavaScript.HasValue Then opt.JavaScriptEnabled = cli.JavaScript.Value
            If cli.DrawBackground.HasValue Then opt.DrawBackground = cli.DrawBackground.Value
            If cli.EmbedFonts.HasValue Then opt.EmbedFonts = cli.EmbedFonts.Value
            If cli.KeepTextsTogether.HasValue Then opt.KeepTextsTogether = cli.KeepTextsTogether.Value
            If cli.KeepImagesTogether.HasValue Then opt.KeepImagesTogether = cli.KeepImagesTogether.Value
            If cli.RenderOnTimeout.HasValue Then opt.RenderPageOnTimeout = cli.RenderOnTimeout.Value
            If cli.SinglePage.HasValue Then opt.GenerateSinglePagePdf = cli.SinglePage.Value
            If cli.DenyLocalFileAccess.HasValue Then opt.DenyLocalFileAccess = cli.DenyLocalFileAccess.Value

            ' css media type
            If Not String.IsNullOrEmpty(cli.CssMediaType) Then _
                opt.CssMediaType = ParseEnum(Of HtmlToPdfCssMediaType)(cli.CssMediaType, opt.CssMediaType)

            ' PDF standard / color space
            If Not String.IsNullOrEmpty(cli.PdfStandard) Then _
                opt.PdfStandard = ParseEnum(Of PdfStandard)(cli.PdfStandard, opt.PdfStandard)
            If Not String.IsNullOrEmpty(cli.ColorSpace) Then _
                opt.ColorSpace = ParseEnum(Of PdfColorSpace)(cli.ColorSpace, opt.ColorSpace)

            ' document information
            If cli.Title IsNot Nothing Then opt.PdfDocumentInformation.Title = cli.Title
            If cli.Author IsNot Nothing Then opt.PdfDocumentInformation.Author = cli.Author
            If cli.Subject IsNot Nothing Then opt.PdfDocumentInformation.Subject = cli.Subject
            If cli.Keywords IsNot Nothing Then opt.PdfDocumentInformation.Keywords = cli.Keywords

            ' security
            If cli.UserPassword IsNot Nothing Then opt.SecurityOptions.UserPassword = cli.UserPassword
            If cli.OwnerPassword IsNot Nothing Then opt.SecurityOptions.OwnerPassword = cli.OwnerPassword

            ' Blink / Chromium specific
            If cli.PageRanges IsNot Nothing Then opt.PageRanges = cli.PageRanges
            If cli.WindowStatus IsNot Nothing Then opt.WindowStatus = cli.WindowStatus
            If cli.CustomCss IsNot Nothing Then opt.CustomCSS = cli.CustomCss
            If cli.PostLoadingScript IsNot Nothing Then opt.PostLoadingScript = cli.PostLoadingScript
            If cli.LazyImagesLoading.HasValue Then opt.LazyImagesLoadingEnabled = cli.LazyImagesLoading.Value
            If cli.LazyImagesLoadingDelay.HasValue Then opt.LazyImagesLoadingDelay = cli.LazyImagesLoadingDelay.Value
            If cli.OfflineMode.HasValue Then opt.EnableOfflineMode = cli.OfflineMode.Value
            If cli.ClickElements IsNot Nothing AndAlso cli.ClickElements.Count > 0 Then _
                opt.ClickElementsSelectors = cli.ClickElements.ToArray()

            ' engine binary paths
            If Not String.IsNullOrEmpty(cli.BlinkEnginePath) Then opt.BlinkEnginePath = cli.BlinkEnginePath
            If Not String.IsNullOrEmpty(cli.CefEnginePath) Then opt.CefEnginePath = cli.CefEnginePath

            ' log file (Blink/Chromium native log; useful for Azure diagnostics)
            If Not String.IsNullOrEmpty(cli.LogFile) Then opt.LFN = cli.LogFile

            ' HTTP headers and cookies
            For Each h As KeyValuePair(Of String, String) In cli.HttpHeaders
                opt.HttpHeaders.Add(h.Key, h.Value)
            Next
            For Each c As KeyValuePair(Of String, String) In cli.HttpCookies
                opt.HttpCookies.Add(c.Key, c.Value)
            Next

            ' header / footer (HTML)
            Dim engine As RenderingEngine = opt.RenderingEngine
            If Not String.IsNullOrEmpty(cli.HeaderHtml) OrElse Not String.IsNullOrEmpty(cli.HeaderHtmlFile) Then
                opt.DisplayHeader = True
                If cli.HeaderHeight.HasValue Then converter.Header.Height = cli.HeaderHeight.Value

                Dim headerHtml As PdfHtmlSection
                If Not String.IsNullOrEmpty(cli.HeaderHtmlFile) Then
                    headerHtml = New PdfHtmlSection(cli.HeaderHtmlFile)
                Else
                    headerHtml = New PdfHtmlSection(cli.HeaderHtml, String.Empty)
                End If
                headerHtml.RenderingEngine = engine
                headerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit
                converter.Header.Add(headerHtml)
            End If

            If Not String.IsNullOrEmpty(cli.FooterHtml) OrElse Not String.IsNullOrEmpty(cli.FooterHtmlFile) OrElse cli.PageNumbers Then
                opt.DisplayFooter = True
                If cli.FooterHeight.HasValue Then converter.Footer.Height = cli.FooterHeight.Value

                If Not String.IsNullOrEmpty(cli.FooterHtml) OrElse Not String.IsNullOrEmpty(cli.FooterHtmlFile) Then
                    Dim footerHtml As PdfHtmlSection
                    If Not String.IsNullOrEmpty(cli.FooterHtmlFile) Then
                        footerHtml = New PdfHtmlSection(cli.FooterHtmlFile)
                    Else
                        footerHtml = New PdfHtmlSection(cli.FooterHtml, String.Empty)
                    End If
                    footerHtml.RenderingEngine = engine
                    footerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit
                    converter.Footer.Add(footerHtml)
                End If

                If cli.PageNumbers Then
                    Dim pageNum As New PdfTextSection(0, 10,
                        "Page: {page_number} of {total_pages}  ",
                        New System.Drawing.Font("Arial", 8))
                    pageNum.HorizontalAlign = PdfTextHorizontalAlign.Right
                    converter.Footer.Add(pageNum)
                End If
            End If

            Console.WriteLine("Converting using " & opt.RenderingEngine.ToString() & " engine...")

            Dim doc As PdfDocument
            If Not String.IsNullOrEmpty(cli.Url) Then
                doc = converter.ConvertUrl(cli.Url)
            ElseIf Not String.IsNullOrEmpty(cli.HtmlFile) Then
                Dim html As String = File.ReadAllText(cli.HtmlFile)
                Dim baseUrl As String = If(Path.GetDirectoryName(Path.GetFullPath(cli.HtmlFile)), String.Empty)
                doc = converter.ConvertHtmlString(html, baseUrl)
            Else
                doc = converter.ConvertHtmlString(cli.HtmlString, String.Empty)
            End If

            Try
                doc.Save(cli.Output)
            Finally
                doc.Close()
            End Try
        End Sub

        Private Function ParseEnum(Of T As Structure)(ByVal value As String, ByVal defaultValue As T) As T
            If String.IsNullOrEmpty(value) Then Return defaultValue
            Return CType([Enum].Parse(GetType(T), value, True), T)
        End Function

        Private Function HasFlag(ByVal args As String(), ByVal name As String) As Boolean
            For Each a As String In args
                If String.Equals(a, name, StringComparison.OrdinalIgnoreCase) Then Return True
            Next
            Return False
        End Function

        Private Sub PrintUsage()
            Console.WriteLine("SelectPdf Command Line Sample")
            Console.WriteLine("Convert an URL or HTML to PDF using SelectPdf.")
            Console.WriteLine()
            Console.WriteLine("Usage:")
            Console.WriteLine("  SelectPdf.Samples.CommandLine [input] [options]")
            Console.WriteLine()
            Console.WriteLine("Input (exactly one required):")
            Console.WriteLine("  --url <url>                URL to convert (e.g. https://selectpdf.com)")
            Console.WriteLine("  --html-file <path>         HTML file to convert")
            Console.WriteLine("  --html <string>            Inline HTML string to convert")
            Console.WriteLine()
            Console.WriteLine("Output:")
            Console.WriteLine("  -o, --output <path>        Output PDF path (default: output.pdf)")
            Console.WriteLine()
            Console.WriteLine("Rendering engine:")
            Console.WriteLine("  --engine <name>            WebKit | WebKitRestricted | Blink | Chromium")
            Console.WriteLine("                             (default: WebKit)")
            Console.WriteLine("  --blink-engine-path <p>    Folder containing Blink chrome.exe")
            Console.WriteLine("  --cef-engine-path <p>      Folder containing SelectPdf.Cef.exe")
            Console.WriteLine()
            Console.WriteLine("Page setup:")
            Console.WriteLine("  --page-size <size>         A4, Letter, Legal, A3, A5, B4, ...")
            Console.WriteLine("  --orientation <o>          Portrait | Landscape")
            Console.WriteLine("  --margin-left <pt>")
            Console.WriteLine("  --margin-right <pt>")
            Console.WriteLine("  --margin-top <pt>")
            Console.WriteLine("  --margin-bottom <pt>")
            Console.WriteLine("  --margins <pt>             Shortcut for all four margins")
            Console.WriteLine()
            Console.WriteLine("Browser viewport:")
            Console.WriteLine("  --web-page-width <px>      Viewport width (default 1024)")
            Console.WriteLine("  --web-page-height <px>     Viewport height (0 = auto)")
            Console.WriteLine("  --web-page-fixed-size <b>  true|false")
            Console.WriteLine("  --auto-fit-width <m>       NoAdjustment | ShrinkOnly | AutoFit")
            Console.WriteLine("  --auto-fit-height <m>      NoAdjustment | ShrinkOnly | AutoFit")
            Console.WriteLine()
            Console.WriteLine("Timing:")
            Console.WriteLine("  --min-page-load-time <s>   Pre-render delay, seconds")
            Console.WriteLine("  --max-page-load-time <s>   Navigation timeout, seconds (default 60)")
            Console.WriteLine("  --max-conversion-time <s>  Print/capture timeout, Chromium only (default 60)")
            Console.WriteLine()
            Console.WriteLine("Compression:")
            Console.WriteLine("  --jpeg-compression <b>     true|false (default true)")
            Console.WriteLine("  --jpeg-compression-level <n>  0..100 (default 10)")
            Console.WriteLine("  --compression-level <l>    None | Normal | Best")
            Console.WriteLine()
            Console.WriteLine("Links / scripting:")
            Console.WriteLine("  --internal-links <b>       true|false (default true)")
            Console.WriteLine("  --external-links <b>       true|false (default true)")
            Console.WriteLine("  --javascript <b>           true|false (default true)")
            Console.WriteLine("  --css-media-type <t>       Screen | Print")
            Console.WriteLine("  --custom-css <css>         Inline CSS injected into the page (Blink/Chromium)")
            Console.WriteLine("  --post-loading-script <js> JS executed after page load (Blink/Chromium)")
            Console.WriteLine("  --click <selector>         CSS selector to click before capture (repeatable, Blink/Chromium)")
            Console.WriteLine("  --window-status <s>        Wait until window.status equals this string (Blink/Chromium)")
            Console.WriteLine("  --offline-mode <b>         Block network before navigation (Blink/Chromium)")
            Console.WriteLine()
            Console.WriteLine("Layout / rendering:")
            Console.WriteLine("  --draw-background <b>      true|false (default true)")
            Console.WriteLine("  --embed-fonts <b>          true|false (default false)")
            Console.WriteLine("  --keep-texts-together <b>  true|false (default true)")
            Console.WriteLine("  --keep-images-together <b> true|false (default false)")
            Console.WriteLine("  --render-on-timeout <b>    true|false (default false)")
            Console.WriteLine("  --single-page <b>          Generate a single-page PDF")
            Console.WriteLine("  --deny-local-file-access <b>  Block file:// URLs")
            Console.WriteLine("  --lazy-images <b>          Lazy image loading delay enabled (default true)")
            Console.WriteLine("  --lazy-images-delay <ms>   Per-page lazy image delay (default 20)")
            Console.WriteLine("  --page-ranges <r>          e.g. ""1-3,5,7-"" (Blink/Chromium)")
            Console.WriteLine()
            Console.WriteLine("PDF standard / color:")
            Console.WriteLine("  --pdf-standard <s>         Full | PdfA1B | PdfA2B | PdfA3B | PdfX1A | ...")
            Console.WriteLine("  --color-space <c>          RGB | CMYK | GrayScale")
            Console.WriteLine()
            Console.WriteLine("Header / footer:")
            Console.WriteLine("  --header-html <html>       Inline header HTML")
            Console.WriteLine("  --header-html-file <path>  Header HTML file")
            Console.WriteLine("  --header-height <pt>")
            Console.WriteLine("  --footer-html <html>       Inline footer HTML")
            Console.WriteLine("  --footer-html-file <path>  Footer HTML file")
            Console.WriteLine("  --footer-height <pt>")
            Console.WriteLine("  --page-numbers             Add 'Page X of Y' to the footer")
            Console.WriteLine()
            Console.WriteLine("Document properties:")
            Console.WriteLine("  --title <s>")
            Console.WriteLine("  --author <s>")
            Console.WriteLine("  --subject <s>")
            Console.WriteLine("  --keywords <s>")
            Console.WriteLine()
            Console.WriteLine("Security:")
            Console.WriteLine("  --user-password <s>        Password required to open the document")
            Console.WriteLine("  --owner-password <s>       Password required to change permissions")
            Console.WriteLine()
            Console.WriteLine("HTTP:")
            Console.WriteLine("  --http-header <name=value> Add HTTP header (repeatable)")
            Console.WriteLine("  --cookie <name=value>      Add HTTP cookie (repeatable)")
            Console.WriteLine()
            Console.WriteLine("Licensing:")
            Console.WriteLine("  --license-key <key>        SelectPdf license key (omit for trial mode)")
            Console.WriteLine()
            Console.WriteLine("Diagnostics:")
            Console.WriteLine("  --log-file <path>          Write Blink/Chromium native engine log to this file")
            Console.WriteLine("                             (forwards to HtmlToPdfOptions.LFN; useful on Azure)")
            Console.WriteLine()
            Console.WriteLine("Examples:")
            Console.WriteLine("  SelectPdf.Samples.CommandLine --url https://selectpdf.com -o site.pdf")
            Console.WriteLine("  SelectPdf.Samples.CommandLine --url https://selectpdf.com --engine Blink \")
            Console.WriteLine("      --page-size A4 --orientation Landscape --margins 20 \")
            Console.WriteLine("      --page-numbers --title ""My report"" -o report.pdf")
            Console.WriteLine("  SelectPdf.Samples.CommandLine --html-file page.html --engine Chromium \")
            Console.WriteLine("      --css-media-type Print --user-password secret -o secured.pdf")
        End Sub

    End Module

    Friend NotInheritable Class CliArgs

        ' Input / output
        Public Url As String
        Public HtmlFile As String
        Public HtmlString As String
        Public Output As String

        ' Engine
        Public Engine As String
        Public BlinkEnginePath As String
        Public CefEnginePath As String

        ' Page
        Public PageSize As String
        Public Orientation As String
        Public MarginLeft As Integer?
        Public MarginRight As Integer?
        Public MarginTop As Integer?
        Public MarginBottom As Integer?

        ' Viewport / fit
        Public WebPageWidth As Integer?
        Public WebPageHeight As Integer?
        Public WebPageFixedSize As Boolean?
        Public AutoFitWidth As String
        Public AutoFitHeight As String

        ' Timing
        Public MinPageLoadTime As Integer?
        Public MaxPageLoadTime As Integer?
        Public MaxConversionTime As Integer?

        ' Compression
        Public JpegCompression As Boolean?
        Public JpegCompressionLevel As Integer?
        Public CompressionLevel As String

        ' Behavior
        Public InternalLinks As Boolean?
        Public ExternalLinks As Boolean?
        Public JavaScript As Boolean?
        Public DrawBackground As Boolean?
        Public EmbedFonts As Boolean?
        Public KeepTextsTogether As Boolean?
        Public KeepImagesTogether As Boolean?
        Public RenderOnTimeout As Boolean?
        Public SinglePage As Boolean?
        Public DenyLocalFileAccess As Boolean?
        Public LazyImagesLoading As Boolean?
        Public OfflineMode As Boolean?
        Public LazyImagesLoadingDelay As Integer?
        Public CssMediaType As String
        Public PdfStandard As String
        Public ColorSpace As String
        Public CustomCss As String
        Public PostLoadingScript As String
        Public PageRanges As String
        Public WindowStatus As String
        Public ClickElements As New List(Of String)()

        ' Header / footer
        Public HeaderHtml As String
        Public HeaderHtmlFile As String
        Public HeaderHeight As Integer?
        Public FooterHtml As String
        Public FooterHtmlFile As String
        Public FooterHeight As Integer?
        Public PageNumbers As Boolean

        ' Document info
        Public Title As String
        Public Author As String
        Public Subject As String
        Public Keywords As String

        ' Security
        Public UserPassword As String
        Public OwnerPassword As String

        ' HTTP
        Public HttpHeaders As New List(Of KeyValuePair(Of String, String))()
        Public HttpCookies As New List(Of KeyValuePair(Of String, String))()

        ' License
        Public LicenseKey As String

        ' Diagnostics
        Public LogFile As String

        Public Shared Function Parse(ByVal args As String()) As CliArgs
            Dim r As New CliArgs()

            Dim i As Integer = 0
            While i < args.Length
                Dim key As String = args(i)
                Select Case key.ToLowerInvariant()
                    Case "--url" : r.Url = ReadValue(args, i, key)
                    Case "--html-file" : r.HtmlFile = ReadValue(args, i, key)
                    Case "--html" : r.HtmlString = ReadValue(args, i, key)

                    Case "-o", "--output" : r.Output = ReadValue(args, i, key)

                    Case "--engine" : r.Engine = ReadValue(args, i, key)
                    Case "--blink-engine-path" : r.BlinkEnginePath = ReadValue(args, i, key)
                    Case "--cef-engine-path" : r.CefEnginePath = ReadValue(args, i, key)

                    Case "--page-size" : r.PageSize = ReadValue(args, i, key)
                    Case "--orientation" : r.Orientation = ReadValue(args, i, key)
                    Case "--margin-left" : r.MarginLeft = ReadInt(args, i, key)
                    Case "--margin-right" : r.MarginRight = ReadInt(args, i, key)
                    Case "--margin-top" : r.MarginTop = ReadInt(args, i, key)
                    Case "--margin-bottom" : r.MarginBottom = ReadInt(args, i, key)
                    Case "--margins"
                        Dim m As Integer = ReadInt(args, i, key)
                        r.MarginLeft = m
                        r.MarginRight = m
                        r.MarginTop = m
                        r.MarginBottom = m

                    Case "--web-page-width" : r.WebPageWidth = ReadInt(args, i, key)
                    Case "--web-page-height" : r.WebPageHeight = ReadInt(args, i, key)
                    Case "--web-page-fixed-size" : r.WebPageFixedSize = ReadBool(args, i, key)
                    Case "--auto-fit-width" : r.AutoFitWidth = ReadValue(args, i, key)
                    Case "--auto-fit-height" : r.AutoFitHeight = ReadValue(args, i, key)

                    Case "--min-page-load-time" : r.MinPageLoadTime = ReadInt(args, i, key)
                    Case "--max-page-load-time" : r.MaxPageLoadTime = ReadInt(args, i, key)
                    Case "--max-conversion-time" : r.MaxConversionTime = ReadInt(args, i, key)

                    Case "--jpeg-compression" : r.JpegCompression = ReadBool(args, i, key)
                    Case "--jpeg-compression-level" : r.JpegCompressionLevel = ReadInt(args, i, key)
                    Case "--compression-level" : r.CompressionLevel = ReadValue(args, i, key)

                    Case "--internal-links" : r.InternalLinks = ReadBool(args, i, key)
                    Case "--external-links" : r.ExternalLinks = ReadBool(args, i, key)
                    Case "--javascript" : r.JavaScript = ReadBool(args, i, key)
                    Case "--draw-background" : r.DrawBackground = ReadBool(args, i, key)
                    Case "--embed-fonts" : r.EmbedFonts = ReadBool(args, i, key)
                    Case "--keep-texts-together" : r.KeepTextsTogether = ReadBool(args, i, key)
                    Case "--keep-images-together" : r.KeepImagesTogether = ReadBool(args, i, key)
                    Case "--render-on-timeout" : r.RenderOnTimeout = ReadBool(args, i, key)
                    Case "--single-page" : r.SinglePage = ReadBool(args, i, key)
                    Case "--deny-local-file-access" : r.DenyLocalFileAccess = ReadBool(args, i, key)
                    Case "--lazy-images" : r.LazyImagesLoading = ReadBool(args, i, key)
                    Case "--lazy-images-delay" : r.LazyImagesLoadingDelay = ReadInt(args, i, key)
                    Case "--offline-mode" : r.OfflineMode = ReadBool(args, i, key)

                    Case "--css-media-type" : r.CssMediaType = ReadValue(args, i, key)
                    Case "--pdf-standard" : r.PdfStandard = ReadValue(args, i, key)
                    Case "--color-space" : r.ColorSpace = ReadValue(args, i, key)
                    Case "--custom-css" : r.CustomCss = ReadValue(args, i, key)
                    Case "--post-loading-script" : r.PostLoadingScript = ReadValue(args, i, key)
                    Case "--page-ranges" : r.PageRanges = ReadValue(args, i, key)
                    Case "--window-status" : r.WindowStatus = ReadValue(args, i, key)
                    Case "--click" : r.ClickElements.Add(ReadValue(args, i, key))

                    Case "--header-html" : r.HeaderHtml = ReadValue(args, i, key)
                    Case "--header-html-file" : r.HeaderHtmlFile = ReadValue(args, i, key)
                    Case "--header-height" : r.HeaderHeight = ReadInt(args, i, key)
                    Case "--footer-html" : r.FooterHtml = ReadValue(args, i, key)
                    Case "--footer-html-file" : r.FooterHtmlFile = ReadValue(args, i, key)
                    Case "--footer-height" : r.FooterHeight = ReadInt(args, i, key)
                    Case "--page-numbers" : r.PageNumbers = True

                    Case "--title" : r.Title = ReadValue(args, i, key)
                    Case "--author" : r.Author = ReadValue(args, i, key)
                    Case "--subject" : r.Subject = ReadValue(args, i, key)
                    Case "--keywords" : r.Keywords = ReadValue(args, i, key)

                    Case "--user-password" : r.UserPassword = ReadValue(args, i, key)
                    Case "--owner-password" : r.OwnerPassword = ReadValue(args, i, key)

                    Case "--http-header" : r.HttpHeaders.Add(ParseKeyValue(ReadValue(args, i, key)))
                    Case "--cookie" : r.HttpCookies.Add(ParseKeyValue(ReadValue(args, i, key)))

                    Case "--license-key" : r.LicenseKey = ReadValue(args, i, key)

                    Case "--log-file" : r.LogFile = ReadValue(args, i, key)

                    Case Else
                        Throw New ArgumentException("Unknown option: " & key)
                End Select

                i += 1
            End While

            Return r
        End Function

        Private Shared Function ReadValue(ByVal args As String(), ByRef i As Integer, ByVal key As String) As String
            If i + 1 >= args.Length Then
                Throw New ArgumentException("Missing value for option " & key)
            End If
            i += 1
            Return args(i)
        End Function

        Private Shared Function ReadInt(ByVal args As String(), ByRef i As Integer, ByVal key As String) As Integer
            Dim v As String = ReadValue(args, i, key)
            Dim n As Integer
            If Not Integer.TryParse(v, n) Then
                Throw New ArgumentException("Option " & key & " expects an integer, got: " & v)
            End If
            Return n
        End Function

        Private Shared Function ReadBool(ByVal args As String(), ByRef i As Integer, ByVal key As String) As Boolean
            Dim v As String = ReadValue(args, i, key)
            Select Case v.ToLowerInvariant()
                Case "true", "1", "yes", "on" : Return True
                Case "false", "0", "no", "off" : Return False
                Case Else
                    Throw New ArgumentException("Option " & key & " expects true/false, got: " & v)
            End Select
        End Function

        Private Shared Function ParseKeyValue(ByVal s As String) As KeyValuePair(Of String, String)
            Dim idx As Integer = s.IndexOf("="c)
            If idx <= 0 Then
                Throw New ArgumentException("Expected name=value, got: " & s)
            End If
            Return New KeyValuePair(Of String, String)(s.Substring(0, idx), s.Substring(idx + 1))
        End Function

    End Class

End Namespace
