package io.sterodium.extensions.node.rmi;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.sikuli.api.ImageTarget;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Collections2.filter;
import static org.apache.commons.io.FileUtils.listFiles;
import static org.apache.commons.io.FileUtils.listFilesAndDirs;
import static org.apache.commons.io.filefilter.DirectoryFileFilter.DIRECTORY;
import static org.apache.commons.io.filefilter.TrueFileFilter.TRUE;

/**
 * @author Mihails Volkovs [email protected]
 *         Date: 20/09/2015
 */
public class TargetFactory {

    private File folderToScan = new File(".");

    public void setImagePrefix(String imagePathPrefix) {
        String imageFolder = !imagePathPrefix.endsWith("/") ? imagePathPrefix + "/" : imagePathPrefix;
        this.folderToScan = new File(imageFolder);
        checkState(folderToScan.exists(), "Folder %s does not exist", folderToScan.getAbsolutePath());
        checkState(folderToScan.isDirectory(), "Folder %s is not a folder", folderToScan.getAbsolutePath());
    }

    public ImageTarget createImageTarget(String imageFile) {
        return new ImageTarget(findImageFile(imageFile));
    }

    @VisibleForTesting
    protected File findImageFile(String imageFile) {
        File foundImageFile = findImageFile(folderToScan, imageFile);
        // creating non existing file to avoid NPE
        return foundImageFile == null ? new File(imageFile) : foundImageFile;
    }

    private File findImageFile(File lookupFolder, String lookupTarget) {
        // given file can contain sub-folders in its name
        String[] paths = lookupTarget.split("[/\\\\]");
        String[] foldersToFind = Arrays.copyOfRange(paths, 0, paths.length - 1);

        for (String folderToFind : foldersToFind) {
            lookupFolder = findFolderRecursively(lookupFolder, folderToFind);
            if (lookupFolder == null) {
                return null;
            }
        }
        lookupTarget = paths[paths.length - 1];

        // finally searching for file name recursively
        Collection<File> files = listFiles(lookupFolder, new NameFileFilter(lookupTarget), TRUE);
        return files.isEmpty() ? null : files.iterator().next();
    }

    private File findFolderRecursively(File folderToScan, final String folderToFind) {
        // filtering out empty arguments
        if (folderToFind.isEmpty()) {
            return folderToScan;
        }

        // collecting all the sub-folders recursively
        Collection<File> foundFolders = listFilesAndDirs(folderToScan, DIRECTORY, TRUE);

        // filtering all the folders by folder name
        foundFolders = filter(foundFolders, new Predicate<File>() {
            @Override
            public boolean apply(File file) {
                String fileName = file.getAbsolutePath();
                return fileName.endsWith("/" + folderToFind) || fileName.endsWith("\\" + folderToFind);
            }
        });
        return foundFolders.isEmpty() ? null : foundFolders.iterator().next();
    }

}