/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.io;

import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;
import net.ranides.assira.collection.ArrayUtils;
import net.ranides.assira.io.FileFilter.AcceptMode;

/**
 * Klasa pomocnicza do pracy z katalogami w systemie plików 
 * (głównie przeglądania ich zawartości).
 * @author ranides
 */
public final class DirectoryHelper {
    
    private DirectoryHelper() { }

    private static List<File> recursiveListFiles(final List<File> target, File path, final FileFilter filter) {
        path.listFiles(new java.io.FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if( filter.accept(pathname) ) {
                    target.add(pathname);
                }
                if( pathname.isDirectory() ) { 
                    recursiveListFiles(target, pathname, filter);
                }
                return false;
            }
        });
        return target;
    }
    
    /**
     * Przeszukuje podany katalog, i zwraca listę wszystkich obiektów, które
     * zostały zaakceptowane przez obiekt {@code filter}. Funkcja respektuje
     * wszystkie flagi {@link FileFilter.AcceptMode}, w szczególności przeszukuje
     * zawartość wgłąb, jeśli tak ustawiono w filtrze. 
     * @param path
     * @param filter
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> list(File path, FileFilter filter) {
        if(filter.getMode().contains(FileFilter.AcceptMode.RECURSIVE)) {
            ArrayList<File> result = new ArrayList<>();
            recursiveListFiles(result, path, filter);
            result.trimToSize();
            return result;
        } else {
            return ArrayUtils.asList(path.listFiles(filter));
        }
    }
    
    /**
     * Wyszukuje plik (nie katalog) na podstawie położenia, tzn sprawdza, czy ścieżka pliku 
     * (ale nie absolutna) pasuje do podanego wyrażenia regularnego.
     * @param path
     * @param pattern
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> listFiles(File path, Pattern pattern) {
        FileFilters.Regex filter = new FileFilters.Regex(EnumSet.of(AcceptMode.FILE));
        filter.include(pattern);
        return list(path, filter);
    }
    
    /**
     * Wyszukuje plik (nie katalog) na podstawie położenia, tzn sprawdza, czy ścieżka pliku 
     * (ale nie absolutna) pasuje do podanej maski.
     * @param path
     * @param wildcard
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> listFiles(File path, String wildcard) {
        FileFilters.Wildcard filter = new FileFilters.Wildcard(EnumSet.of(AcceptMode.FILE));
        filter.include(wildcard);
        return list(path, filter);
    }
    
    /**
     * Wyszukuje katalog na podstawie położenia, tzn sprawdza, czy ścieżka katalogu 
     * (ale nie absolutna) pasuje do podanego wyrażenia regularnego.
     * @param path
     * @param pattern
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> listDirs(File path, Pattern pattern) {
        FileFilters.Regex filter = new FileFilters.Regex(EnumSet.of(AcceptMode.DIRECTORY));
        filter.include(pattern);
        return list(path, filter);
    }
    
    /**
     * Wyszukuje katalog na podstawie położenia, tzn sprawdza, czy ścieżka pliku 
     * (ale nie absolutna) pasuje do podanej maski.
     * @param path
     * @param wildcard
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> listDirs(File path, String wildcard) {
        FileFilters.Wildcard filter = new FileFilters.Wildcard(EnumSet.of(AcceptMode.DIRECTORY));
        filter.include(wildcard);
        return list(path, filter);
    }
    
    /**
     * Wyszukuje plik lub katalog na podstawie nazwy, tzn {@code filter} otrzymuje tylko nazwę pliku, 
     * a nie całą ścieżkę, i na tej podstawie ma filtrować. Ignoruje całkowicie
     * flagi w otrzymanym filtrze.
     * @param path
     * @param filter
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> find(File path, final FileFilter filter) {
        return list(path, new FileFilter(EnumSet.of(AcceptMode.DIRECTORY, AcceptMode.FILE, AcceptMode.RECURSIVE)) {
            @Override
            public boolean acceptFile(File file) {
                return filter.acceptFile(new File(file.getName()));
            }
        });
    }
    
    /**
     * Wyszukuje plik lub katalog na podstawie nazwy, tzn sprawdza, czy nazwa pliku 
     * (a nie cała ścieżka) pasuje do podanej nazwy.
     * @param path
     * @param wildcard
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> find(File path, String wildcard) {
        return find(path, new FileFilters.Wildcard().include(wildcard));
    }
    
    /**
     * Wyszukuje plik lub katalog na podstawie nazwy, tzn sprawdza, czy nazwa pliku 
     * (a nie cała ścieżka) pasuje do podanego wyrażenia regularnego.
     * @param path
     * @param pattern
     * @return lista znalezionych plików. Ścieżki zawierają {@code path}, nie są 
     * jednak zamieniane na lokalizacje absolutne.
     */
    public static List<File> find(File path, Pattern pattern) {
        return find(path, new FileFilters.Regex().include(pattern));
    }
 
}