using System;
using System.Collections.Generic;
using System.IO;
using 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>
    internal static class Program
    {
        private static int Main(string[] args)
        {
            if (args.Length == 0 || HasFlag(args, "--help") || HasFlag(args, "-h") || HasFlag(args, "/?"))
            {
                PrintUsage();
                return 0;
            }

            CliArgs cli;
            try
            {
                cli = CliArgs.Parse(args);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Argument error: " + ex.Message);
                Console.Error.WriteLine("Run with --help to see the list of options.");
                return 2;
            }

            int inputCount = 0;
            if (!string.IsNullOrEmpty(cli.Url)) inputCount++;
            if (!string.IsNullOrEmpty(cli.HtmlFile)) inputCount++;
            if (!string.IsNullOrEmpty(cli.HtmlString)) inputCount++;
            if (inputCount != 1)
            {
                Console.Error.WriteLine("You must specify exactly one of: --url, --html-file, --html");
                return 2;
            }

            if (string.IsNullOrEmpty(cli.Output))
            {
                cli.Output = "output.pdf";
            }

            try
            {
                ConvertToPdf(cli);
                Console.WriteLine("PDF created: " + Path.GetFullPath(cli.Output));
                return 0;
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Conversion failed: " + ex.Message);
                Console.Error.WriteLine(ex.ToString());
                return 1;
            }
        }

        private static void ConvertToPdf(CliArgs cli)
        {
            if (!string.IsNullOrEmpty(cli.LicenseKey))
            {
                GlobalProperties.LicenseKey = cli.LicenseKey;
            }

            HtmlToPdf converter = new HtmlToPdf();
            HtmlToPdfOptions opt = converter.Options;

            // rendering engine
            opt.RenderingEngine = ParseEnum(cli.Engine, RenderingEngine.WebKit);

            // page size, orientation, margins
            opt.PdfPageSize = ParseEnum(cli.PageSize, PdfPageSize.A4);
            opt.PdfPageOrientation = ParseEnum(cli.Orientation, PdfPageOrientation.Portrait);
            if (cli.MarginLeft.HasValue) opt.MarginLeft = cli.MarginLeft.Value;
            if (cli.MarginRight.HasValue) opt.MarginRight = cli.MarginRight.Value;
            if (cli.MarginTop.HasValue) opt.MarginTop = cli.MarginTop.Value;
            if (cli.MarginBottom.HasValue) opt.MarginBottom = cli.MarginBottom.Value;

            // browser viewport
            if (cli.WebPageWidth.HasValue) opt.WebPageWidth = cli.WebPageWidth.Value;
            if (cli.WebPageHeight.HasValue) opt.WebPageHeight = cli.WebPageHeight.Value;
            if (cli.WebPageFixedSize.HasValue) opt.WebPageFixedSize = cli.WebPageFixedSize.Value;

            // auto fit
            if (!string.IsNullOrEmpty(cli.AutoFitWidth))
                opt.AutoFitWidth = ParseEnum(cli.AutoFitWidth, opt.AutoFitWidth);
            if (!string.IsNullOrEmpty(cli.AutoFitHeight))
                opt.AutoFitHeight = ParseEnum(cli.AutoFitHeight, opt.AutoFitHeight);

            // timing
            if (cli.MinPageLoadTime.HasValue) opt.MinPageLoadTime = cli.MinPageLoadTime.Value;
            if (cli.MaxPageLoadTime.HasValue) opt.MaxPageLoadTime = cli.MaxPageLoadTime.Value;
            if (cli.MaxConversionTime.HasValue) opt.MaxConversionTime = cli.MaxConversionTime.Value;

            // compression
            if (cli.JpegCompression.HasValue) opt.JpegCompressionEnabled = cli.JpegCompression.Value;
            if (cli.JpegCompressionLevel.HasValue) opt.JpegCompressionLevel = cli.JpegCompressionLevel.Value;
            if (!string.IsNullOrEmpty(cli.CompressionLevel))
                opt.PdfCompressionLevel = ParseEnum(cli.CompressionLevel, opt.PdfCompressionLevel);

            // links
            if (cli.InternalLinks.HasValue) opt.InternalLinksEnabled = cli.InternalLinks.Value;
            if (cli.ExternalLinks.HasValue) opt.ExternalLinksEnabled = cli.ExternalLinks.Value;

            // scripting and rendering behavior
            if (cli.JavaScript.HasValue) opt.JavaScriptEnabled = cli.JavaScript.Value;
            if (cli.DrawBackground.HasValue) opt.DrawBackground = cli.DrawBackground.Value;
            if (cli.EmbedFonts.HasValue) opt.EmbedFonts = cli.EmbedFonts.Value;
            if (cli.KeepTextsTogether.HasValue) opt.KeepTextsTogether = cli.KeepTextsTogether.Value;
            if (cli.KeepImagesTogether.HasValue) opt.KeepImagesTogether = cli.KeepImagesTogether.Value;
            if (cli.RenderOnTimeout.HasValue) opt.RenderPageOnTimeout = cli.RenderOnTimeout.Value;
            if (cli.SinglePage.HasValue) opt.GenerateSinglePagePdf = cli.SinglePage.Value;
            if (cli.DenyLocalFileAccess.HasValue) opt.DenyLocalFileAccess = cli.DenyLocalFileAccess.Value;

            // css media type
            if (!string.IsNullOrEmpty(cli.CssMediaType))
                opt.CssMediaType = ParseEnum(cli.CssMediaType, opt.CssMediaType);

            // PDF standard / color space
            if (!string.IsNullOrEmpty(cli.PdfStandard))
                opt.PdfStandard = ParseEnum(cli.PdfStandard, opt.PdfStandard);
            if (!string.IsNullOrEmpty(cli.ColorSpace))
                opt.ColorSpace = ParseEnum(cli.ColorSpace, opt.ColorSpace);

            // document information
            if (cli.Title != null) opt.PdfDocumentInformation.Title = cli.Title;
            if (cli.Author != null) opt.PdfDocumentInformation.Author = cli.Author;
            if (cli.Subject != null) opt.PdfDocumentInformation.Subject = cli.Subject;
            if (cli.Keywords != null) opt.PdfDocumentInformation.Keywords = cli.Keywords;

            // security
            if (cli.UserPassword != null) opt.SecurityOptions.UserPassword = cli.UserPassword;
            if (cli.OwnerPassword != null) opt.SecurityOptions.OwnerPassword = cli.OwnerPassword;

            // Blink / Chromium specific
            if (cli.PageRanges != null) opt.PageRanges = cli.PageRanges;
            if (cli.WindowStatus != null) opt.WindowStatus = cli.WindowStatus;
            if (cli.CustomCss != null) opt.CustomCSS = cli.CustomCss;
            if (cli.PostLoadingScript != null) opt.PostLoadingScript = cli.PostLoadingScript;
            if (cli.LazyImagesLoading.HasValue) opt.LazyImagesLoadingEnabled = cli.LazyImagesLoading.Value;
            if (cli.LazyImagesLoadingDelay.HasValue) opt.LazyImagesLoadingDelay = cli.LazyImagesLoadingDelay.Value;
            if (cli.OfflineMode.HasValue) opt.EnableOfflineMode = cli.OfflineMode.Value;
            if (cli.ClickElements != null && cli.ClickElements.Count > 0)
                opt.ClickElementsSelectors = cli.ClickElements.ToArray();

            // engine binary paths
            if (!string.IsNullOrEmpty(cli.BlinkEnginePath)) opt.BlinkEnginePath = cli.BlinkEnginePath;
            if (!string.IsNullOrEmpty(cli.CefEnginePath)) opt.CefEnginePath = cli.CefEnginePath;

            // log file (Blink/Chromium native log; useful for Azure diagnostics)
            if (!string.IsNullOrEmpty(cli.LogFile)) opt.LFN = cli.LogFile;

            // HTTP headers and cookies
            foreach (KeyValuePair<string, string> h in cli.HttpHeaders)
                opt.HttpHeaders.Add(h.Key, h.Value);
            foreach (KeyValuePair<string, string> c in cli.HttpCookies)
                opt.HttpCookies.Add(c.Key, c.Value);

            // header / footer (HTML)
            RenderingEngine engine = opt.RenderingEngine;
            if (!string.IsNullOrEmpty(cli.HeaderHtml) || !string.IsNullOrEmpty(cli.HeaderHtmlFile))
            {
                opt.DisplayHeader = true;
                if (cli.HeaderHeight.HasValue) converter.Header.Height = cli.HeaderHeight.Value;

                PdfHtmlSection headerHtml = !string.IsNullOrEmpty(cli.HeaderHtmlFile)
                    ? new PdfHtmlSection(cli.HeaderHtmlFile)
                    : new PdfHtmlSection(cli.HeaderHtml, string.Empty);
                headerHtml.RenderingEngine = engine;
                headerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
                converter.Header.Add(headerHtml);
            }

            if (!string.IsNullOrEmpty(cli.FooterHtml) || !string.IsNullOrEmpty(cli.FooterHtmlFile) || cli.PageNumbers)
            {
                opt.DisplayFooter = true;
                if (cli.FooterHeight.HasValue) converter.Footer.Height = cli.FooterHeight.Value;

                if (!string.IsNullOrEmpty(cli.FooterHtml) || !string.IsNullOrEmpty(cli.FooterHtmlFile))
                {
                    PdfHtmlSection footerHtml = !string.IsNullOrEmpty(cli.FooterHtmlFile)
                        ? new PdfHtmlSection(cli.FooterHtmlFile)
                        : new PdfHtmlSection(cli.FooterHtml, string.Empty);
                    footerHtml.RenderingEngine = engine;
                    footerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
                    converter.Footer.Add(footerHtml);
                }

                if (cli.PageNumbers)
                {
                    PdfTextSection pageNum = new PdfTextSection(0, 10,
                        "Page: {page_number} of {total_pages}  ",
                        new System.Drawing.Font("Arial", 8));
                    pageNum.HorizontalAlign = PdfTextHorizontalAlign.Right;
                    converter.Footer.Add(pageNum);
                }
            }

            Console.WriteLine("Converting using " + opt.RenderingEngine + " engine...");

            PdfDocument doc;
            if (!string.IsNullOrEmpty(cli.Url))
            {
                doc = converter.ConvertUrl(cli.Url);
            }
            else if (!string.IsNullOrEmpty(cli.HtmlFile))
            {
                string html = File.ReadAllText(cli.HtmlFile);
                string baseUrl = Path.GetDirectoryName(Path.GetFullPath(cli.HtmlFile)) ?? string.Empty;
                doc = converter.ConvertHtmlString(html, baseUrl);
            }
            else
            {
                doc = converter.ConvertHtmlString(cli.HtmlString, string.Empty);
            }

            try
            {
                doc.Save(cli.Output);
            }
            finally
            {
                doc.Close();
            }
        }

        private static T ParseEnum<T>(string value, T defaultValue) where T : struct
        {
            if (string.IsNullOrEmpty(value)) return defaultValue;
            return (T)Enum.Parse(typeof(T), value, true);
        }

        private static bool HasFlag(string[] args, string name)
        {
            foreach (string a in args)
            {
                if (string.Equals(a, name, StringComparison.OrdinalIgnoreCase)) return true;
            }
            return false;
        }

        private static void 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");
        }

        private sealed class CliArgs
        {
            // Input / output
            public string Url;
            public string HtmlFile;
            public string HtmlString;
            public string Output;

            // Engine
            public string Engine;
            public string BlinkEnginePath;
            public string CefEnginePath;

            // Page
            public string PageSize;
            public string Orientation;
            public int? MarginLeft, MarginRight, MarginTop, MarginBottom;

            // Viewport / fit
            public int? WebPageWidth;
            public int? WebPageHeight;
            public bool? WebPageFixedSize;
            public string AutoFitWidth, AutoFitHeight;

            // Timing
            public int? MinPageLoadTime, MaxPageLoadTime, MaxConversionTime;

            // Compression
            public bool? JpegCompression;
            public int? JpegCompressionLevel;
            public string CompressionLevel;

            // Behavior
            public bool? InternalLinks, ExternalLinks, JavaScript, DrawBackground, EmbedFonts;
            public bool? KeepTextsTogether, KeepImagesTogether, RenderOnTimeout, SinglePage;
            public bool? DenyLocalFileAccess, LazyImagesLoading, OfflineMode;
            public int? LazyImagesLoadingDelay;
            public string CssMediaType;
            public string PdfStandard;
            public string ColorSpace;
            public string CustomCss;
            public string PostLoadingScript;
            public string PageRanges;
            public string WindowStatus;
            public List<string> ClickElements = new List<string>();

            // Header / footer
            public string HeaderHtml, HeaderHtmlFile;
            public int? HeaderHeight;
            public string FooterHtml, FooterHtmlFile;
            public int? FooterHeight;
            public bool PageNumbers;

            // Document info
            public string Title, Author, Subject, Keywords;

            // Security
            public string UserPassword, OwnerPassword;

            // HTTP
            public List<KeyValuePair<string, string>> HttpHeaders = new List<KeyValuePair<string, string>>();
            public List<KeyValuePair<string, string>> HttpCookies = new List<KeyValuePair<string, string>>();

            // License
            public string LicenseKey;

            // Diagnostics
            public string LogFile;

            public static CliArgs Parse(string[] args)
            {
                CliArgs r = new CliArgs();

                for (int i = 0; i < args.Length; i++)
                {
                    string key = args[i];
                    switch (key.ToLowerInvariant())
                    {
                        case "--url": r.Url = ReadValue(args, ref i, key); break;
                        case "--html-file": r.HtmlFile = ReadValue(args, ref i, key); break;
                        case "--html": r.HtmlString = ReadValue(args, ref i, key); break;

                        case "-o":
                        case "--output": r.Output = ReadValue(args, ref i, key); break;

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

                        case "--page-size": r.PageSize = ReadValue(args, ref i, key); break;
                        case "--orientation": r.Orientation = ReadValue(args, ref i, key); break;
                        case "--margin-left": r.MarginLeft = ReadInt(args, ref i, key); break;
                        case "--margin-right": r.MarginRight = ReadInt(args, ref i, key); break;
                        case "--margin-top": r.MarginTop = ReadInt(args, ref i, key); break;
                        case "--margin-bottom": r.MarginBottom = ReadInt(args, ref i, key); break;
                        case "--margins":
                        {
                            int m = ReadInt(args, ref i, key);
                            r.MarginLeft = r.MarginRight = r.MarginTop = r.MarginBottom = m;
                            break;
                        }

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

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

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

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

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

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

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

                        case "--user-password": r.UserPassword = ReadValue(args, ref i, key); break;
                        case "--owner-password": r.OwnerPassword = ReadValue(args, ref i, key); break;

                        case "--http-header": r.HttpHeaders.Add(ParseKeyValue(ReadValue(args, ref i, key))); break;
                        case "--cookie": r.HttpCookies.Add(ParseKeyValue(ReadValue(args, ref i, key))); break;

                        case "--license-key": r.LicenseKey = ReadValue(args, ref i, key); break;

                        case "--log-file": r.LogFile = ReadValue(args, ref i, key); break;

                        default:
                            throw new ArgumentException("Unknown option: " + key);
                    }
                }

                return r;
            }

            private static string ReadValue(string[] args, ref int i, string key)
            {
                if (i + 1 >= args.Length)
                    throw new ArgumentException("Missing value for option " + key);
                i++;
                return args[i];
            }

            private static int ReadInt(string[] args, ref int i, string key)
            {
                string v = ReadValue(args, ref i, key);
                int n;
                if (!int.TryParse(v, out n))
                    throw new ArgumentException("Option " + key + " expects an integer, got: " + v);
                return n;
            }

            private static bool ReadBool(string[] args, ref int i, string key)
            {
                string v = ReadValue(args, ref i, key);
                switch (v.ToLowerInvariant())
                {
                    case "true": case "1": case "yes": case "on": return true;
                    case "false": case "0": case "no": case "off": return false;
                    default:
                        throw new ArgumentException("Option " + key + " expects true/false, got: " + v);
                }
            }

            private static KeyValuePair<string, string> ParseKeyValue(string s)
            {
                int idx = s.IndexOf('=');
                if (idx <= 0)
                    throw new ArgumentException("Expected name=value, got: " + s);
                return new KeyValuePair<string, string>(s.Substring(0, idx), s.Substring(idx + 1));
            }
        }
    }
}
