/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ghidra.util.filechooser;

import java.io.File;
import java.io.FileFilter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import ghidra.util.Msg;
import utilities.util.reflection.ReflectionUtilities;

/**
 * A convenience implementation of FileFilter that filters out
 * all files except for those type extensions that it knows about.
 * <p>
 * Extensions are of the type "foo" (no leading dot). Case is ignored.
 * <p>
 * Example - create a new filter that filters out all files
 * but gif and jpg image files:
 * <pre>
 *     GhidraFileChooser chooser = new GhidraFileChooser();
 *     chooser.addFileFilter(ExtensionFilFilter.forExtensions("JPEG and GIF Images", "gif", "jpg"));
 *</pre>
 */
public class ExtensionFileFilter implements GhidraFileFilter {

	/**
	 * Creates a {@link ExtensionFileFilter} in a varargs friendly way.
	 *
	 * @param description String description of this set of file extensions.
	 * @param exts variable length list of file extensions, without leading dot.
	 * @return new {@link ExtensionFileFilter} instance.
	 */
	public static ExtensionFileFilter forExtensions(String description, String... exts) {
		ExtensionFileFilter eff = new ExtensionFileFilter(exts, description);
		return eff;
	}

	private List<String> extensions;
	private String description;
	private String fullDescription;

	/**
	 * Creates a file filter that accepts the given file type.
	 * <p>
	 * Example: new ExtensionFileFilter("jpg", "JPEG Images");
	 *
	 * @param extension file extension to match, without leading dot
	 * @param description descriptive string of the filter
	 */
	public ExtensionFileFilter(String extension, String description) {
		this(new String[] { extension }, description);
	}

	/**
	 * Creates a file filter from the given string array and description.
	 * <p>
	 * Example: new ExtensionFileFilter(String {"gif", "jpg"}, "Gif and JPG Images");
	 *
	 * @param filters array of file name extensions, each without a leading dot
	 * @param description descriptive string of the filter
	 */
	public ExtensionFileFilter(String[] filters, String description) {
		this(Arrays.stream(filters), description);
	}

	/**
	 * Creates a file filter from the given string {@link Collection} and description.
	 * <p>
	 * Example: new ExtensionFileFilter(List.of("gif", "jpg"), "Gif and JPG Images");
	 *
	 * @param filters array of file name extensions, each without a leading dot
	 * @param description descriptive string of the filter
	 */
	public ExtensionFileFilter(Collection<String> filters, String description) {
		this(filters.stream(), description);
	}

	/**
	 * Creates a file filter from the given string {@link Stream} and description.
	 * <p>
	 * Example: new ExtensionFileFilter(Stream.of("gif", "jpg"), "Gif and JPG Images");
	 *
	 * @param filters {@link List} of file name extensions, each without a leading dot
	 * @param description descriptive string of the filter
	 */
	private ExtensionFileFilter(Stream<String> filters, String description) {
		this.extensions = filters.map(ExtensionFileFilter::clean).collect(Collectors.toList());
		this.description = description;
	}

	private static String clean(String ext) {
		String lc = ext.toLowerCase();
		if (lc.startsWith(".")) {
			Msg.error(ExtensionFileFilter.class, "Extensions cannot start with '.': " + ext,
				ReflectionUtilities.createJavaFilteredThrowable());
			return lc.substring(1);
		}
		return lc;
	}

	/**
	 * Return true if this file should be shown in the directory pane,
	 * false if it shouldn't.
	 *
	 * Files that begin with "." are ignored.
	 *
	 * @see FileFilter#accept
	 */
	@Override
	public boolean accept(File f, GhidraFileChooserModel model) {
		if (f == null) {
			return false;
		}
		if (model.isDirectory(f)) {
			return true;
		}
		if (extensions.isEmpty()) {
			return true;
		}

		String filename = f.getName().toLowerCase();
		if (filename.startsWith(".")) {
			return false; // assuming we don't want to allow hidden files?
		}

		int n = filename.length();
		for (String ext : extensions) {
			if (ext.length() >= n) {
				continue; // >= since we will add the '.'
			}

			if (filename.endsWith('.' + ext)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public String getDescription() {
		if (fullDescription == null) {
			fullDescription = Objects.requireNonNullElse(description, "");

			// add prettified extensions to the description string
			fullDescription += " (";
			fullDescription += extensions.isEmpty()
					? "*.*"
					: extensions.stream().map(s -> "*." + s).collect(Collectors.joining(","));
			fullDescription += ")";
		}
		return fullDescription;
	}
}
