/*
* Created on Apr 13, 2003
*/
package CH.ifa.draw.io;

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

import de.renew.ioontology.ExtensionFileFilter;
import de.renew.ioontology.FileFilter;

/**
 * SimpleFileFilters can be combined into new combinations of FileFilters.
 * By adding SimpleFileFilters the Combination will filter all except for those listed.
 * CombinationFileFilter can be added to a new Combination but not to the same.
 * Beware of multiple linked recursions.
 *
 * @author Lawrence Cabac, Konstantin Moellers (1kmoelle)
 */
public class CombinationFileFilter extends SimpleFileFilter implements java.io.FileFilter {
    private final HashSet<SimpleFileFilter> filterList;
    private SimpleFileFilter preferredFileFilter;
    private String description;
    private boolean allowDirectory = true;
    private boolean allowHidden = false;

    /**
     * Constructs a new CombinationFileFilter with a description and sets up a list to add or remove filters from.
     *
     * @param description A text that describes the CombinationFileFilter
     */
    public CombinationFileFilter(String description) {
        filterList = new HashSet<>();
        setDescription(description);
    }

    @Override
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * Adds the given {@link SimpleFileFilter} to this {@link CombinationFileFilter}.
     * @param ff the given filter
     * @return {@code true} if the filter was added successfully, {@code false} otherwise
     */
    public boolean add(SimpleFileFilter ff) {
        boolean result = false;
        if (!contains(ff)) {
            if (filterList.isEmpty()) {
                preferredFileFilter = ff;
            }
            result = filterList.add(ff);
        }
        return result;
    }

    /**
     * Adds the given {@link FileFilter} to this {@link CombinationFileFilter}.
     * @param ff the given filter
     * @return {@code true} if the filter was added successfully, {@code false} otherwise
     */
    public boolean add(FileFilter ff) {
        SimpleFileFilter simpleFileFilter = new SimpleFileFilter() {
            public boolean accept(File f) {
                return ff.accept(f);
            }
        };
        simpleFileFilter.setDescription(ff.getDescription());
        return add(simpleFileFilter);
    }

    /**
     * Adds the given {@link de.renew.ioontology.ExtensionFileFilter} to this {@link CombinationFileFilter}.
     * @param ff the given filter
     * @return {@code true} if the filter was added successfully, {@code false} otherwise
     */
    public boolean add(ExtensionFileFilter ff) {
        return add(new SimpleFileFilter(ff.getExtension(), ff.getDescription()));
    }

    /**
     * Adds the given {@link FileFilter}s to this {@link CombinationFileFilter}.
     * @param filters the given filters
     * @return {@code true} if the filter was added successfully, {@code false} otherwise
     */
    public boolean addAll(Collection<? extends FileFilter> filters) {
        boolean result = false;
        for (FileFilter ff : filters) {
            if (ff instanceof ExtensionFileFilter extensionFileFilter) {
                result = add(extensionFileFilter) || result; // This "add" method is the original method which does not map the FileFilter.
            } else {
                result = add(ff) || result; // This "add" method is the new wrapper method that wraps FileFilter into SimpleFileFilter.
            }
        }
        return result;
    }

    /**
     * Checks if the given {@link SimpleFileFilter} is already contained in this {@link CombinationFileFilter}.
     * @param filter the given filter
     * @return {@code true} if the filter is already contained in this {@link CombinationFileFilter}, {@code false} otherwise
     */
    public boolean contains(SimpleFileFilter filter) {
        boolean result = false;
        for (SimpleFileFilter ff : filterList) {
            if (filter != null && filter.equals(ff)) {
                result = true;
                break;
            }
        }
        return result;
    }

    /**
     * Checks if the given {@link ExtensionFileFilter} is already contained in this {@link CombinationFileFilter}.
     * @param filter the given filter
     * @return {@code true} if the filter is already contained in this {@link CombinationFileFilter}, {@code false} otherwise
     */
    public boolean contains(ExtensionFileFilter filter) {
        return contains(new SimpleFileFilter(filter.getExtension(), filter.getDescription()));
    }

    /**
     * Removes the given {@link SimpleFileFilter} from this {@link CombinationFileFilter}.
     * @param ff the given filter
     */
    public void remove(SimpleFileFilter ff) {
        filterList.remove(ff);
    }

    /**
     * Removes the given {@link ExtensionFileFilter} from this {@link CombinationFileFilter}.
     * @param ff the given filter
     */
    public void remove(ExtensionFileFilter ff) {
        filterList.remove(new SimpleFileFilter(ff.getExtension(), ff.getDescription()));
    }

    /**
     * Gets the set of {@link SimpleFileFilter}s contained in this {@link CombinationFileFilter}.
     * @return the set of {@link SimpleFileFilter}s contained in this {@link CombinationFileFilter}
     */
    public HashSet<SimpleFileFilter> getFileFilters() {
        return filterList;
    }

    @Override
    public String getExtension() {
        return getPreferedFileFilter().getExtension();
    }

    @Override
    public void allowHidden(boolean b) {
        allowHidden = b;
    }

    @Override
    public void allowDirectory(boolean b) {
        allowDirectory = b;
    }

    @Override
    public boolean isHiddenAllowed() {
        return allowHidden;
    }

    @Override
    public boolean isDirectoryAllowed() {
        return allowDirectory;
    }

    /* (non-Javadoc)
     * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
     */
    @Override
    public boolean accept(File f) {
        // Never accept null files.
        if (f == null) {
            return false;
        }

        // Check for a hidden file.
        if (!allowHidden && (f.isHidden() || f.getName().startsWith("."))) {
            return false;
        }

        // Check for directories.
        if (f.isDirectory()) {
            return allowDirectory;
        }

        // Check if one of the contained filters accepts the file.
        for (SimpleFileFilter filter : filterList) {
            if (filter.accept(f)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Gets the preferred {@link SimpleFileFilter}.
     * @return the preferred {@link SimpleFileFilter}
     */
    public SimpleFileFilter getPreferedFileFilter() {
        return preferredFileFilter;
    }

    /**
     * Sets the preferred {@link SimpleFileFilter} to the given filter.
     * @param filter the given filter
     */
    public void setPreferedFileFilter(SimpleFileFilter filter) {
        preferredFileFilter = filter;
    }

    /**
     * Sets the preferred {@link FileFilter} to the given filter.
     * @param filter the given filter
     */
    public void setPreferedFileFilter(FileFilter filter) {
        preferredFileFilter = new SimpleFileFilter() {
            @Override
            public boolean accept(File f) {
                return filter.accept(f);
            }
        };
        preferredFileFilter.setDescription(filter.getDescription());
    }

    /**
     * Checks whether the set of filters in this {@link CombinationFileFilter} is empty.
     * @return {@code true} if the set of filters is empty, {@code false} otherwise
     */
    public boolean isEmpty() {
        return filterList.isEmpty();
    }

    /* (non-Javadoc)
    * @see javax.swing.filechooser.FileFilter#getDescription()
    */
    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CombinationFileFilter other) {
            return getDescription().equals(other.getDescription())
                && getFileFilters().equals(other.getFileFilters());
        }

        return false;
    }

    @Override
    public int hashCode() {
        return CombinationFileFilter.class.hashCode() ^ getDescription().hashCode()
            ^ getFileFilters().hashCode();
    }
}