class SlstrPduStitcher {

    public static File createStitchedSlstrL1BFile(File targetDirectory, File[] slstrProductFiles, String productName,
                                                  ProgressMonitor pm)
            throws IllegalArgumentException, IOException, PDUStitchingException,
            ParserConfigurationException,  TransformerException {
        Assert.notNull(slstrProductFiles);
        final Logger logger = EngineConfig.instance().logger();
        Validator.validateSlstrProductFiles(slstrProductFiles);

        final Date now = Calendar.getInstance().getTime();
        if (slstrProductFiles.length == 1) {
            final File originalParentDirectory = slstrProductFiles[0].getParentFile();
            final String parentDirectoryName = originalParentDirectory.getName();
            File stitchedParentDirectory;
            if (productName == null) {
                stitchedParentDirectory = new File(targetDirectory, parentDirectoryName);
            } else {
                stitchedParentDirectory = new File(targetDirectory, productName);
            }
            if (stitchedParentDirectory.exists()) {
                throw new PDUStitchingException("Target file directory already exists");
            }
            Files.copy(originalParentDirectory.getParentFile().toPath(), stitchedParentDirectory.toPath());
            final File[] files = originalParentDirectory.listFiles();
            long productSize = 0;
            if (files != null) {
                for (File originalFile : files) {
                    final File newFile = new File(stitchedParentDirectory, originalFile.getName());
                    Files.copy(originalFile.toPath(), newFile.toPath());
                    productSize += newFile.length();
                }
            }
            return createManifestFile(slstrProductFiles, stitchedParentDirectory, now, productSize);
        }
        SlstrNameDecomposition[] slstrNameDecompositions = new SlstrNameDecomposition[slstrProductFiles.length];
        Document[] manifestDocuments = new Document[slstrProductFiles.length];
        List<String> ncFileNames = new ArrayList<>();
        Map<String, ImageSize[]> idToImageSizes = new HashMap<>();
        for (int i = 0; i < slstrProductFiles.length; i++) {
            slstrNameDecompositions[i] = decomposeSlstrName(slstrProductFiles[i].getParentFile().getName());
            manifestDocuments[i] = createXmlDocument(new FileInputStream(slstrProductFiles[i]));
            final ImageSize[] imageSizes = ImageSizeHandler.extractImageSizes(manifestDocuments[i]);
            for (ImageSize imageSize : imageSizes) {
                if (idToImageSizes.containsKey(imageSize.getIdentifier())) {
                    idToImageSizes.get(imageSize.getIdentifier())[i] = imageSize;
                } else {
                    final ImageSize[] mapImageSizes = new ImageSize[slstrProductFiles.length];
                    mapImageSizes[i] = imageSize;
                    idToImageSizes.put(imageSize.getIdentifier(), mapImageSizes);
                }
            }
            collectFiles(ncFileNames, manifestDocuments[i]);
        }
        Validator.validateOrbitReference(manifestDocuments);
        Validator.validateAdjacency(manifestDocuments);
        String stitchedProductFileName = productName;
        if (stitchedProductFileName == null) {
            stitchedProductFileName = createParentDirectoryNameOfStitchedFile(slstrNameDecompositions, now);
        }
        File stitchedProductFileParentDirectory = new File(targetDirectory, stitchedProductFileName);
        if (stitchedProductFileParentDirectory.exists()) {
            throw new PDUStitchingException("Target file directory already exists");
        }
        if (!stitchedProductFileParentDirectory.mkdirs()) {
            throw new PDUStitchingException("Could not create product directory");
        }
        Map<String, ImageSize> idToTargetImageSize = new HashMap<>();
        for (String id : idToImageSizes.keySet()) {
            idToTargetImageSize.put(id, ImageSizeHandler.createTargetImageSize(idToImageSizes.get(id)));
        }
        long productSize = 0;
        File manifestFile;
        pm.beginTask("Stitching SLSTR L1B Product Dissemination Units", ncFileNames.size() + 1);
        try {
            for (int i = 0; i < ncFileNames.size(); i++) {
                final String ncFileName = ncFileNames.get(i);
                String[] splitFileName = ncFileName.split("/");
                final String displayFileName = splitFileName[splitFileName.length - 1];
                pm.setSubTaskName(MessageFormat.format("Stitching ''{0}''", displayFileName));
                List<File> ncFiles = new ArrayList<>();
                List<ImageSize> imageSizeList = new ArrayList<>();
                String id = ncFileName.substring(ncFileName.length() - 5, ncFileName.length() - 3);
                if (id.equals("tx")) {
                    id = "tn";
                }
                ImageSize targetImageSize = idToTargetImageSize.get(id);
                if (targetImageSize == null) {
                    targetImageSize = NULL_IMAGE_SIZE;
                }
                ImageSize[] imageSizes = idToImageSizes.get(id);
                if (imageSizes == null) {
                    imageSizes = new ImageSize[ncFileNames.size()];
                    Arrays.fill(imageSizes, NULL_IMAGE_SIZE);
                }
                for (int j = 0; j < slstrProductFiles.length; j++) {
                    File slstrProductFile = slstrProductFiles[j];
                    File ncFile = new File(slstrProductFile.getParentFile(), ncFileName);
                    if (ncFile.exists()) {
                        ncFiles.add(ncFile);
                        imageSizeList.add(imageSizes[j]);
                    }
                }
                if (ncFiles.size() > 0) {
                    final File[] ncFilesArray = ncFiles.toArray(new File[ncFiles.size()]);
                    final ImageSize[] imageSizeArray = imageSizeList.toArray(new ImageSize[imageSizeList.size()]);
                    logger.log(Level.INFO, "Stitch " + displayFileName);
                    NcFileStitcher.stitchNcFiles(ncFileName, stitchedProductFileParentDirectory, now,
                            ncFilesArray, targetImageSize, imageSizeArray);
                    productSize += new File(stitchedProductFileParentDirectory, ncFileName).length();
                }
                if (pm.isCanceled()) {
                    return null;
                }
                pm.worked(1);
            }
            pm.setSubTaskName("Stitching manifest");
            logger.log(Level.INFO, "Stitch manifest");
            manifestFile = createManifestFile(slstrProductFiles, stitchedProductFileParentDirectory, now, productSize);
            pm.worked(1);
        } finally {
            pm.done();
        }
        return manifestFile;
    }

}