using System;
using System.Collections;
using System.IO;
using Ecrion.Ultrascale;

namespace XFDesktopEngineSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // print Usage
            if (args.Length == 0)
            {
                printUsage();
                return;
            }

            // print Examples, Usage, Parameters
            if (args.Length == 1 && args[0] == "-help")
            {
                printHelp();
                return;
            }

            render(args);
        }

        static int render(string[] args)
        {
            try
            {
                String inputFile = args[0];
                String paramsList = "";

                for (int i = 0; i != args.Length; ++i)
                {
                    if (args[i].StartsWith("-parameters"))
                    {
                        paramsList = args[i];
                        args[i] = String.Empty;
                        break;
                    }
                }

                CommandLineParameters cp = new CommandLineParameters();

                cp.Parse(paramsList, true);

                String outputFile = "";

                bool foundInputFileParameter = false;

                //determine input file
                for (int i = 0; i != args.Length; ++i)
                {
                    if (args[i] == "-fo")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set input file.");
                        else
                        {
                            inputFile = args[i + 1];
                            cp.inputFormat = Engine.InputFormat.XSLFO;
                            foundInputFileParameter = true;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-xml")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set input file.");
                        else
                        {
                            inputFile = args[i + 1];
                            cp.inputFormat = Engine.InputFormat.XML;
                            foundInputFileParameter = true;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-in")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set input file.");
                        else
                        {
                            inputFile = args[i + 1];
                            cp.inputFormat = detectInputFormat(inputFile);
                            foundInputFileParameter = true;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }
                }

                if (!foundInputFileParameter)
                {
                    inputFile = args[0];
                    args[0] = String.Empty;
                    cp.inputFormat = detectInputFormat(inputFile);
                }

                if (inputFile.Length == 0)
                {
                    Console.Error.WriteLine("Could not set input file");
                }

                //determine xsl template
                for (int i = 0; i != args.Length; ++i)
                {
                    if (args[i] == "-xsl")
                        if (i + 1 > (int)(args.Length - 1))
                        {
                            Console.Error.WriteLine("Could not set XSL template.");
                            return 1;
                        }
                        else
                        {
                            String xslFile = args[i + 1];
                            try
                            {
                                cp.templateName = xslFile;
                                Console.Out.WriteLine("Using template '" + xslFile + "'");
                            }
                            catch (Exception e)
                            {
                                Console.Error.WriteLine(String.Format("Could not load XSL template: {0}", e.Message));
                                return 1;
                            }
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }
                }

                if (cp.inputFormat == Engine.InputFormat.XML &&
                    CommandLineParameters.IsNullOrEmpty(cp.templateName) &&
                    CommandLineParameters.IsNullOrEmpty(cp.serverTemplateID))
                {
                    // XML is also the default extension for WordML
                    // since we don't have any templates assigned, we'll assume WordML
                    cp.inputFormat = Engine.InputFormat.WordML;
                }

                bool autoRenameOutput = false;

                //determine output file
                for (int i = 0; i != args.Length; ++i)
                {
                    if (args[i] == "-out")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = detectOutputFormat(outputFile);
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-pdf")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.PDF;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-xps")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.XPS;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-afp")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.AFP;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-xap")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.Silverlight;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-ps")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.POSTSCRIPT;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-tiff")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.TIFF;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-jpeg")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.JPEG;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-gif")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.GIF;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-png")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.PNG;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-svg")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.SVG;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-pptx")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.PPTX;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-epub")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.EPUB;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-wordml")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.WordML;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }

                    if (args[i] == "-html")
                        if (i + 1 > (int)(args.Length - 1))
                            Console.Error.WriteLine("Could not set output file.");
                        else
                        {
                            outputFile = args[i + 1];
                            cp.outputFormat = Engine.OutputFormat.HTML;
                            args[i] = String.Empty;
                            args[i + 1] = String.Empty;
                        }
                }

                if (IsNullOrEmpty(outputFile))
                {
                    for (int i = 0; i != args.Length; ++i)
                    {
                        if (!IsNullOrEmpty(args[i]))
                        {
                            outputFile = args[i];
                            cp.outputFormat = detectOutputFormat(outputFile);
                            args[i] = "";
                            break;
                        }
                    }
                }


                if (IsNullOrEmpty(outputFile))
                {
                    outputFile = Path.ChangeExtension(inputFile, ".pdf");
                    cp.outputFormat = Engine.OutputFormat.PDF;
                    autoRenameOutput = true;
                }

                Stream outStm = null;
                String[] inputs = inputFile.Split(';');

                bool isMerge = cp.isMergeOperation;

                if (autoRenameOutput && isMerge)
                {
                    // merge context, create a single PDF named after first file
                    outputFile = Path.ChangeExtension(inputs[0], ".pdf");
                    autoRenameOutput = false;
                }

                RenderingParameters p = cp.GetRenderingParameters();

                for (int i = 0; i != inputs.Length; ++i)
                {
                    String outFile = outputFile;

                    if (autoRenameOutput)
                        outFile = Path.ChangeExtension(inputs[i], ".pdf");

                    String inFile = Path.GetFullPath(inputs[i]);
                    String baseUrl = Path.GetDirectoryName(inFile);

                    p.InputBytesID = Path.GetFileName(inFile);
                    p.OutputBytesID = Path.GetFileName(outFile);
                    p.BaseUrl = baseUrl;

                    // input format
                    Engine.InputFormat inputFormat = cp.inputFormat;
                    if (inputFormat == Engine.InputFormat.Unknown)
                        inputFormat = detectInputFormat(inFile);
                    XmlDataSource xmlDataSource = new XmlDataSource(inFile, cp.inputFormat);

                    // output format
                    if (p.OutputFormat == Engine.OutputFormat.Unknown)
                        p.OutputFormat = detectOutputFormat(outFile);

                    if (isMerge && (i == (inputs.Length - 1)))
                        p.IsLastDocumentInMergeContext = true;

                    if (isMerge)
                        Console.Out.WriteLine("Adding '{0}' into '{1}'", inFile, outFile);
                    else
                        Console.Out.WriteLine("Rendering '{0}' to '{1}'", inFile, outFile);

                    DateTime start = DateTime.Now;

                    
                    Engine eng = new Engine();

                    // If this is a merge op, create the output stream only once;
                    // if this is a direct print op, don't create it at all
                    //if (p.PrinterName.Length == 0 && (p.MergeContextID == null || outStm == null))

                    if (p.PrinterSettings.PrinterName.Length == 0)
                    {
                        using (outStm = new FileStream(outFile, (i != 0 && isMerge) ? FileMode.Append : FileMode.Create, FileAccess.Write))
                        {
                            eng.Render(xmlDataSource, outStm, p);
                        }
                    }
                    else
                        eng.Render(xmlDataSource, outStm, p);

                    DateTime end = DateTime.Now;
                    TimeSpan duration = end - start;

                    Console.WriteLine("Took {0:0.000} seconds", duration.TotalSeconds);
                }

                Console.Out.WriteLine("Document(s) rendered successfully");
            }
            catch (Exception e)
            {
                // Report the error into the output
                Console.Error.WriteLine("Error: " + e.Message);
                if (e.InnerException != null)
                {
                    Console.Error.WriteLine("--------------- Inner Exception ---------------");
                    Console.Error.WriteLine(e.InnerException.Message);
                }
                return 1;
            }

            return 0;
        }

        private static Engine.OutputFormat detectOutputFormat(String outFile)
        {
            if (outFile.EndsWith(".pdf"))
                return Engine.OutputFormat.PDF;
            else if (outFile.EndsWith(".xml"))
                return Engine.OutputFormat.WordML;
            else if (outFile.EndsWith(".xps"))
                return Engine.OutputFormat.XPS;
            else if (outFile.EndsWith(".tiff") || outFile.EndsWith(".tif"))
                return Engine.OutputFormat.TIFF;
            else if (outFile.EndsWith(".gif"))
                return Engine.OutputFormat.GIF;
            else if (outFile.EndsWith(".jpg") || outFile.EndsWith(".jpeg"))
                return Engine.OutputFormat.JPEG;
            else if (outFile.EndsWith(".png"))
                return Engine.OutputFormat.PNG;
            else if (outFile.EndsWith(".pptx"))
                return Engine.OutputFormat.PPTX;
            else if (outFile.EndsWith(".epub"))
                return Engine.OutputFormat.EPUB;

            else
                throw new Exception("Unrecognized output file extension.");
        }

        private static Engine.InputFormat detectInputFormat(String inFile)
        {
            if (inFile.EndsWith(".xml"))
                return Engine.InputFormat.XML;
            else if (inFile.EndsWith(".doc"))
                return Engine.InputFormat.WordML;
            else if (inFile.EndsWith(".fo"))
                return Engine.InputFormat.XSLFO;
            else if (inFile.EndsWith(".svg"))
                return Engine.InputFormat.SVG;
            else if (inFile.EndsWith(".docx"))
                return Engine.InputFormat.DocX;
            else if (inFile.EndsWith(".pdf"))
                return Engine.InputFormat.PDF;
            else if (inFile.EndsWith(".html") || inFile.EndsWith(".htm"))
                return Engine.InputFormat.HTML;
            else if (inFile.EndsWith(".eps") || inFile.EndsWith(".ps"))
                return Engine.InputFormat.EPS;
            else if (inFile.EndsWith(".dal"))
                return Engine.InputFormat.DAL;
            else
                throw new Exception("Unrecognized input file extension.");
        }

        static void printHelp()
        {
            printUsage();
            Console.Error.WriteLine("\nEXAMPLES:");
            Console.Error.WriteLine("Render an XSL-FO to PDF:");
            Console.Error.WriteLine("    XFDesktopEngineSample.exe -fo HelloWorld.fo -pdf HelloWorld.pdf");
            Console.Error.WriteLine("Render an XML to PDF with an XFD template:");
            Console.Error.WriteLine("    XFDesktopEngineSample.exe -xml HelloWorld.xml -xsl HelloWorld.xfd -pdf HelloWorld.pdf");
            Console.Error.WriteLine("Merging multiple documents into a PDF:");
            Console.Error.WriteLine("    XFDesktopEngineSample.exe -in Hello1.fo;Hello2.fo;Hello3.docx -pdf HelloWorld.pdf -parameters:merge=true");

            Console.Error.WriteLine("\nPARAMETERS:");
            CommandLineParameters.PrintSupportedParameters();
        }

        static void printUsage()
        {
            Console.Error.WriteLine("\nXFDesktopEngineSample.exe - Renders XSL-FO into PDF, DOCX, PPTX ");
            Console.Error.WriteLine("\nUSAGE:");
            Console.Error.WriteLine("XFDesktopEngineSample.exe \n" +
                            "    [-fo|-xml|-in] <inFile1>;<inFile2>;...;<inFileN> \n" +
                            "    [-xsl <templateFile>] \n" +
                            "    [-pdf|-xps|-foout|-tiff|-pptx|-epub|-out <outFile>] \n" +
                            "    [-server <server>] \n" +
                            "    [-port <port>] \n" +
                            "    [-help]\n" +
                            "    [-parameters:\"name1=value1;name2=value2;...;nameN=valueN\"]\n");
        }
        private static bool IsNullOrEmpty(String str)
        {	//.NET 1.1 doesn't have String.IsNullOrEmpty
            return str == null || str.Length == 0;
        }
    }
}
