package de.renew.ioontology.importing;

import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import CH.ifa.draw.io.CombinationFileFilter;
import CH.ifa.draw.io.SimpleFileFilter;
import de.renew.ioontology.ExtensionFileFilter;
import de.renew.ioontology.FileFilter;
import de.renew.ioontology.MultiExtensionFileFilter;
import de.renew.ioontology.MultiExtensionFileFilterImpl;

/**
 * Abstract implementation of ImportFormatMulti and extension of the class ImportFormatAbstract.
 * @param <T> The type of the object that is the result of the import.
 */
public abstract class ImportFormatMultiAbstract<T> extends ImportFormatAbstract<T>
    implements ImportFormatMulti<T>
{
    // All ImportFormats that have been added (MultiFormats do count as one element).
    private final List<ImportFormat<T>> _formats;

    /**
     * Constructs ImportFormatMultiAbstract with a given format name and a new file filter.
     * @param name the given format name
     * @param filterName the name of the new file filter
     */
    public ImportFormatMultiAbstract(String name, String filterName) {
        super(name, new MultiExtensionFileFilterImpl(filterName));
        _formats = new LinkedList<ImportFormat<T>>();
    }

    // Methods

    /**
     * Returns a MultiExtensionFileFilter that contains all added FileFilters
     * (Does not contain other MultiExtensionFileFilter but their elements).
     * Ensures the result is not {@code null}.
     * @return the MultiExtensionFileFilter for the ImportFormat
     */
    private MultiExtensionFileFilter multiFileFilter() {
        MultiExtensionFileFilter result = (MultiExtensionFileFilter) fileFilter();
        Objects.requireNonNull(result, "Failure in ImportFormatAbstract: result == null");
        return result;
    }

    /**
     * Adds a fileFilter to the CombinationFileFilter (If the fileFilter itself is a
     * CombinationFileFilter only its elements are added).
     * @param fileFilter the FileFilter to be added
     */
    protected void addFileFilter(FileFilter fileFilter) {
        if (fileFilter instanceof CombinationFileFilter comFileFilter) {
            Iterator<SimpleFileFilter> filters = comFileFilter.getFileFilters().iterator();
            while (filters.hasNext()) {
                FileFilter element = filters.next();
                addFileFilter(element);
            }
        } else if (fileFilter instanceof MultiExtensionFileFilter multiFilter) {
            for (ExtensionFileFilter filter : multiFilter.getFileFilters()) {
                addFileFilter(filter);
            }
        } else {
            if (fileFilter instanceof ExtensionFileFilter) {
                multiFileFilter().add((ExtensionFileFilter) fileFilter);
            }
        }
    }

    /**
     * Removes the fileFilter from the CombinationFileFilter (If fileFilter is
     * a CombinationFileFiler all its elements are removed).
     * @param fileFilter FileFilter to be removed
     */
    protected void removeFileFilter(FileFilter fileFilter) {
        if (fileFilter instanceof CombinationFileFilter comFileFilter) {
            Iterator<SimpleFileFilter> filters = comFileFilter.getFileFilters().iterator();
            while (filters.hasNext()) {
                FileFilter element = filters.next();
                removeFileFilter(element);
            }
        } else if (fileFilter instanceof MultiExtensionFileFilter multiFilter) {
            for (ExtensionFileFilter filter : multiFilter.getFileFilters()) {
                removeFileFilter(filter);
            }
        } else {
            if (fileFilter instanceof ExtensionFileFilter) {
                multiFileFilter().remove((ExtensionFileFilter) fileFilter);
            }
        }
    }

    // ---------------------------------------------------------------------
    // Implementation of the ImportFormatMulti Interface
    // ---------------------------------------------------------------------


    @Override
    public void addImportFormat(ImportFormat<T> format) {
        Objects.requireNonNull(format, "Failure in ImportFormatMultiAbstract: format == null");
        _formats.add(format);
        addFileFilter(format.fileFilter());
    }

    @Override
    public List<ImportFormat<T>> getImportFormats() {
        return _formats;
    }

    @Override
    public void removeImportFormat(ImportFormat<T> format) {
        Objects.requireNonNull(format, "Failure in ImportFormatMultiAbstract: format == null");
        removeFileFilter(format.fileFilter());
        _formats.remove(format);
    }

    @Override
    public List<T> importFiles(URL[] paths) throws Exception {
        List<T> result = new ArrayList<T>();
        for (int posfiles = 0; posfiles < paths.length; posfiles++) {
            List<ImportFormat<T>> formats = getImportFormats();
            for (int posformat = 0; posformat < formats.size(); posformat++) {
                if (formats.get(posformat).canImport(paths[posfiles])) {
                    URL[] urls = new URL[1];
                    urls[0] = paths[posfiles];
                    result.addAll(formats.get(posformat).importFiles(urls));
                }
            }
        }
        return result;
    }
}