/*
 * Copyright (c) 1997-2024 IDRsolutions (https://www.idrsolutions.com)
 */
package org.jpedal.examples;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * Helper class for converting Office documents to PDF using LibreOffice.
 */
public final class DocumentToPDFConverter {

    private static final String TEMP_DIR;
    static {
        String tempDir = System.getProperty("java.io.tmpdir");
        if (!tempDir.endsWith("/"&& !tempDir.endsWith("\\")) {
            tempDir += System.getProperty("file.separator");
        }
        TEMP_DIR = tempDir;
    }

    private DocumentToPDFConverter() {
    }

    /**
     * Uses LibreOffice to convert an office document to PDF using the provided LibreOffice executable path.
     <p>
     * Creates a file with the same name (with .pdf extension) in the same directory as the source file.
     <p>
     * If the output file already exists, it will not be overwritten and an IOException will be thrown.
     *
     @param document                  Office document to convert to PDF
     @param libreOfficeExecutablePath Absolute path for the LibreOffice executable to use to convert the document
     @param allowOverwrite            Boolean flag on whether the output file can be overwritten
     @return The exit code of the LibreOffice process (usually 0 regardless of success or failure)
     @throws IOException          If the LibreOffice executable was not found, the document does not exist, the document does
     *                              not have a valid file extension, or the output file already exists (and allowOverwrite
     *                              is set to false)
     @throws InterruptedException If the conversion process gets interrupted
     */
    public static int convert(final File document, final String libreOfficeExecutablePath, final boolean allowOverwritethrows IOException, InterruptedException {

        if (!document.exists()) {
            throw new FileNotFoundException(document.getAbsolutePath());
        }

        final int idx = document.getAbsolutePath().lastIndexOf('.');
        if (idx < 1) {
            throw new IOException("Document does not have a valid file extension: " + document.getAbsolutePath());
        }

        final String outputDocumentPath = document.getAbsolutePath().substring(0, idx".pdf";
        final File outputDocument = new File(outputDocumentPath);
        if (outputDocument.exists()) {
            if (allowOverwrite) {
                outputDocument.delete();
            else {
                throw new IOException("Output file already exists: " + outputDocumentPath);
            }
        }

        // Use a unique profile directory to allow multiple conversions simultaneously
        final String uniqueLOProfile = (TEMP_DIR + "LO-" + UUID.randomUUID()).replace('\\''/')// Requires a url (not a path)

        final ProcessBuilder pb = new ProcessBuilder(libreOfficeExecutablePath,
                "-env:UserInstallation=file:///" + uniqueLOProfile,
                "--headless""--convert-to""pdf", document.getName());

        pb.directory(document.getParentFile());

        try {
            return pb.start().waitFor();
        finally {
            deleteFolder(new File(uniqueLOProfile));
        }
    }

    /**
     * Uses LibreOffice to convert an office document to PDF using the provided LibreOffice executable path.
     <p>
     * Creates a file with the same name (with .pdf extension) in the same directory as the source file.
     <p>
     * If the output file already exists, it will not be overwritten and an IOException will be thrown.
     *
     @param document                  Office document to convert to PDF
     @param libreOfficeExecutablePath Absolute path for the LibreOffice executable to use to convert the document
     @return The exit code of the LibreOffice process (usually 0 regardless of success or failure)
     @throws IOException          If the LibreOffice executable was not found, the document does not exist, the document does
     *                              not have a valid file extension, or the output file already exists (will not overwrite)
     @throws InterruptedException If the conversion process gets interrupted
     */
    public static int convert(final File document, final String libreOfficeExecutablePaththrows IOException, InterruptedException {
        return convert(document, libreOfficeExecutablePath, false);
    }

    private static final String[] FILE_TYPES = {
            "ODT""FODT""ODS""FODS""ODP""FODP""ODB""ODG""FODG""ODF"// LibreOffice
            "SXW""STW""SXC""STC""SXI""STI""SXD""STD""SXM"// OpenOffice
            "DOCX""XLSX""PPTX",
            "DOC""DOT",
            "PPT""PPS""POT",
            "XLS""XLW""XLT"
    };

    private static final Set<String> FILE_TYPES_LIST = new HashSet<>(Arrays.asList(FILE_TYPES));

    /**
     * Returns whether the provided file has a convertible file type
     *
     @param file file to check
     @return whether provided file is convertible
     */
    public static boolean hasConvertibleFileType(final File file) {
        final int idx = file.getAbsolutePath().lastIndexOf('.');
        return idx > && FILE_TYPES_LIST.contains(file.getAbsolutePath().substring(idx + 1).toUpperCase());
    }

    private static void deleteFolder(final File dirPath) {
        final File[] files = dirPath.listFiles();
        if (files != null) {
            for (final File file : files) {
                if (file.isDirectory()) {
                    deleteFolder(file);
                }
                file.delete();
            }
        }
        dirPath.delete();
    }
}