package mkl.testarea.itext5.merge;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;

import org.junit.BeforeClass;
import org.junit.Test;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSmartCopy;

/**
 * This test focuses on {@link PdfSmartCopy} issues.
 * 
 * @author mkl
 */
public class SmartMerging
{
    final static File RESULT_FOLDER = new File("target/test-outputs", "merge");

    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        RESULT_FOLDER.mkdirs();
    }

    /**
     * <a href="http://stackoverflow.com/questions/32267542/pdf-merge-issue-in-itextsharp-pdf-looks-distorted-after-merge">
     * Pdf Merge issue in itextsharp - PDF looks distorted after Merge
     * </a>
     * <br>
     * <a href="https://www.dropbox.com/s/l479kvar16p9p57/input.pdf?dl=0">
     * input.pdf
     * </a>
     * <p>
     * This test method together with the static methods {@link #ExtractPages(String, int, int)}
     * and {@link #Merge(File[])} constitute a fairly 1:1 translation of the OP's C#/iTextSharp
     * code. Unfortunately, though, it does not reproduce the error reproducibly occurring in the
     * iTextSharp version.
     * </p>
     */
    @Test
    public void testSplitAndRemerge() throws IOException, DocumentException
    {
        String inputDocPath = "input.pdf";

        byte[] part1 = ExtractPages(inputDocPath, 1, 2);
        File outputPath1 = new File(RESULT_FOLDER, "part1.pdf");
        Files.write(outputPath1.toPath(), part1);

        byte[] part2 = ExtractPages(inputDocPath, 3, 0);
        File outputPath2 = new File(RESULT_FOLDER, "part2.pdf");
        Files.write(outputPath2.toPath(), part2);

        byte[] merged = Merge(new File[] { outputPath1, outputPath2 });

        File mergedPath = new File(RESULT_FOLDER, "output.pdf");
        Files.write(mergedPath.toPath(), merged);
    }

    public static byte[] Merge(File[] documentPaths) throws IOException, DocumentException
    {
        byte[] mergedDocument;

        try (ByteArrayOutputStream memoryStream = new ByteArrayOutputStream())
        {
            Document document = new Document();
            PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document, memoryStream);
            document.open();

            for (File docPath : documentPaths)
            {
                PdfReader reader = new PdfReader(docPath.toString());
                try
                {
                    reader.consolidateNamedDestinations();
                    int numberOfPages = reader.getNumberOfPages();
                    for (int page = 0; page < numberOfPages;)
                    {
                        PdfImportedPage pdfImportedPage = pdfSmartCopy.getImportedPage(reader, ++page);
                        pdfSmartCopy.addPage(pdfImportedPage);
                    }
                }
                finally
                {
                    reader.close();
                }
            }

            document.close();
            mergedDocument = memoryStream.toByteArray();
        }

        return mergedDocument;
    }

    public static byte[] ExtractPages(String pdfDocument, int startPage, int endPage) throws IOException, DocumentException
    {
        try (InputStream pdfDocumentStream = SmartMerging.class.getResourceAsStream(pdfDocument))
        {
            PdfReader reader = new PdfReader(pdfDocumentStream);
            int numberOfPages = reader.getNumberOfPages();
            int endPageResolved = endPage > 0 ? endPage : numberOfPages;
            if (startPage > numberOfPages || endPageResolved > numberOfPages)
                System.err.printf("Error: page indices (%s, %s) out of bounds. Document has {2} pages.", startPage, endPageResolved, numberOfPages);

            byte[] outputDocument;
            try (ByteArrayOutputStream msOut = new ByteArrayOutputStream())
            {
                Document doc = new Document();
                PdfCopy pdfCopyProvider = new PdfCopy(doc, msOut);
                doc.open();
                for (int i = startPage; i <= endPageResolved; i++)
                {
                    PdfImportedPage page = pdfCopyProvider.getImportedPage(reader, i);
                    pdfCopyProvider.addPage(page);
                }
                doc.close();
                reader.close();
                outputDocument = msOut.toByteArray();
            }

            return outputDocument;
        }
    }
}