package com.technia.tif.enovia.job.executors.file.external;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.function.Supplier;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.technia.common.xml.XMLException;
import com.technia.common.xml.XMLReader;
import com.technia.tif.core.annotation.CopyToDocumentation;
import com.technia.tif.core.transfer.DestinationOrPath;
import com.technia.tif.enovia.job.executors.file.StatusLog;

/**
 * PDF Conversion using the Adlib Folder connector.
 *
 * @since 2020-02-12
 */
public class AdlibPDFConverter extends ExtCreatePDF {

    private static final Logger logger = LoggerFactory.getLogger(AdlibPDFConverter.class);

    private Supplier<Path> input;

    private Supplier<Path> output;

    private Supplier<Path> error;

    private long pollTimeout = 5_000; // 5 seconds

    private long maxTimeout = 10 * 60 * 1000; // 10 minutes

    @Override
    protected boolean handleElement(XMLReader reader, String elementName) throws XMLException {
        switch (elementName) {
            case "Input":
                this.input = parseDestinationOrPath(reader);
                return true;
            case "Output":
                this.output = parseDestinationOrPath(reader);
                return true;
            case "Error":
                this.error = parseDestinationOrPath(reader);
                return true;
            case "PollTimeout":
                this.pollTimeout = parseTime(reader.getElementText(), this.pollTimeout);
                return true;
            case "MaxTimeout":
                this.maxTimeout = parseTime(reader.getElementText(), this.maxTimeout);
                return true;
            default:
                return super.handleElement(reader, elementName);
        }
    }

    @Override
    protected void process(File input, String pdfFileName, StatusLog log) throws IOException {
        log.log("Adlib PDF conversion with folder connector");

        File adlibInputDir = getDir(this.input, true);
        File adlibOutputDir = getDir(this.output, true);
        File adlibErrorDir = getDir(this.error, false);

        log.log("input = %s, output = %s, error = %s", adlibInputDir, adlibOutputDir, adlibErrorDir);

        // Step 1: Move file to ADLIB Input Folder
        final String workFileName = String.format("%s.%s", UUID.randomUUID().toString(),
                FilenameUtils.getExtension(input.getName()));
        log.log("Work file name = %s", workFileName);
        FileUtils.moveFile(input, new File(adlibInputDir, workFileName));

        // Step 2: Poll ADLIB output folder for completion (and optionally the
        // error folder)
        log.log("Waiting for completion...");
        File resultFile = poll(workFileName, adlibOutputDir, adlibErrorDir, pollTimeout, maxTimeout);

        // Step 3: Move the final file from ADLIB output folder to the place,
        // where TIF expects to find it.
        FileUtils.moveFile(resultFile, new File(input.getParentFile(), pdfFileName));
    }

    protected File poll(String workFileName,
                        File outDir,
                        File errDir,
                        long pollTimeout,
                        long maxTimeout) throws IOException {

        File errorFile = errDir != null ? new File(errDir, workFileName) : null;
        File outputFile = new File(outDir, workFileName + ".pdf");
        final long startTime = System.currentTimeMillis();
        while (true) {
            try {
                if (logger.isDebugEnabled()) {
                    logger.debug("Sleeping for {} ms.", pollTimeout);
                }
                Thread.sleep(pollTimeout);
            } catch (InterruptedException e) {
            }
            if (errorFile != null && errorFile.exists()) {
                throw new IOException("Adlib processing error");
            } else if (outputFile.exists()) {
                return outputFile;
            } else if (System.currentTimeMillis() - startTime > maxTimeout) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Aborting. Exceeding max timeout {} ms.", maxTimeout);
                }
                return null;
            }
        }
    }

    protected static long parseTime(String s, long defaultValue) {
        if (NumberUtils.isCreatable(s)) {
            return NumberUtils.createLong(s);
        }
        return defaultValue;
    }

    protected static Supplier<Path> parseDestinationOrPath(XMLReader reader) {
        String destinationId = reader.getAttributeValue("destinationId");
        if (destinationId != null) {
            return new DestinationOrPath(destinationId);
        }
        String path = reader.getAttributeValue("path");
        if (path != null) {
            return new DestinationOrPath(Paths.get(path));
        }
        return null;
    }

    protected static File getDir(Supplier<Path> pathSupplier, boolean mustExist) {
        if (pathSupplier == null) {
            if (mustExist) {
                throw new IllegalStateException("Folder/destination id is missing!");
            }
            return null;
        }
        Path path = pathSupplier.get();
        if (Files.isDirectory(path)) {
            return path.toFile();
        }
        return null;
    }
}