package CH.ifa.draw.io.importFormats;

import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import de.renew.draw.storables.ontology.Drawing;
import de.renew.ioontology.FileFilter;

/**
 * Class that allows mapping import formats objects which are of the old CH type {@link ImportFormat}
 * to the new type {@link de.renew.ioontology.importing.ImportFormat}.
 * Every mapped object is held in a Map to allow returning the same object when mapping the same input.
 * @deprecated This type offers a temporary solution to allow using old export format types. It will later be removed.
 */
@Deprecated(since = "5.0", forRemoval = true)
@SuppressWarnings("removal")
public class ImportFormatMapper {

    /** Map containing all import formats that were previously already mapped.*/
    private static final Map<ImportFormat, de.renew.ioontology.importing.ImportFormat<Drawing>> IMPORT_FORMAT_MAP;

    static {
        IMPORT_FORMAT_MAP = new HashMap<>();
    }

    private ImportFormatMapper() {}

    /**
     * Maps the given import format.
     *
     * @param oldFormat the format to be mapped
     * @return the result of the mapping
     */
    public static de.renew.ioontology.importing.ImportFormat<Drawing> mapImportFormat(
        ImportFormat oldFormat)
    {
        if (IMPORT_FORMAT_MAP.containsKey(oldFormat)) {
            return IMPORT_FORMAT_MAP.get(oldFormat);
        }

        if (oldFormat instanceof ImportFormatMulti oldMultiFormat) {
            de.renew.ioontology.importing.ImportFormatMulti<Drawing> newFormat =
                mapMultiFormat(oldMultiFormat);
            IMPORT_FORMAT_MAP.put(oldFormat, newFormat);
            return newFormat;
        }

        de.renew.ioontology.importing.ImportFormat<Drawing> format = mapSingleFormat(oldFormat);
        IMPORT_FORMAT_MAP.put(oldFormat, format);
        return format;
    }

    /**
     * Maps the given import format.
     *
     * @param newFormat the format to be mapped
     * @return the result of the mapping
     */
    public static ImportFormat mapImportFormat(
        de.renew.ioontology.importing.ImportFormat<Drawing> newFormat)
    {
        if (IMPORT_FORMAT_MAP.containsValue(newFormat)) {
            return IMPORT_FORMAT_MAP.entrySet().stream().filter(e -> e.getValue().equals(newFormat))
                .findFirst().get().getKey();
        }

        if (newFormat instanceof de.renew.ioontology.importing.ImportFormatMulti<Drawing> newMultiFormat) {
            return mapMultiFormat(newMultiFormat);
        }

        return mapSingleFormat(newFormat);
    }

    private static de.renew.ioontology.importing.ImportFormatMulti<Drawing> mapMultiFormat(
        ImportFormatMulti oldFormat)
    {
        return new de.renew.ioontology.importing.ImportFormatMulti<>() {
            @Override
            public void addImportFormat(
                de.renew.ioontology.importing.ImportFormat<Drawing> importFormat)
            {
                oldFormat.addImportFormat(mapImportFormat(importFormat));
            }

            @Override
            public void removeImportFormat(
                de.renew.ioontology.importing.ImportFormat<Drawing> importFormat)
            {
                oldFormat.removeImportFormat(mapImportFormat(importFormat));
            }

            @Override
            public List<de.renew.ioontology.importing.ImportFormat<Drawing>> getImportFormats() {
                return Arrays.stream(oldFormat.allImportFormats())
                    .map(ImportFormatMapper::mapImportFormat).toList();
            }

            @Override
            public List<Drawing> importFiles(URL[] paths) throws Exception {
                return oldFormat.importFiles(paths);
            }

            @Override
            public FileFilter fileFilter() {
                if (oldFormat.fileFilter() instanceof FileFilter ff) {
                    return ff;
                }

                return new InternalFileFilter(oldFormat.fileFilter());
            }

            @Override
            public String formatName() {
                return oldFormat.formatName();
            }

            @Override
            public boolean canImport(URL path) {
                return oldFormat.canImport(path);
            }

            @Override
            public boolean canImport(URI path) {
                return oldFormat.canImport(path);
            }
        };
    }

    private static de.renew.ioontology.importing.ImportFormat<Drawing> mapSingleFormat(
        ImportFormat oldFormat)
    {
        return new de.renew.ioontology.importing.ImportFormat<>() {
            @Override
            public List<Drawing> importFiles(URL[] paths) throws Exception {
                return oldFormat.importFiles(paths);
            }

            @Override
            public FileFilter fileFilter() {
                if (oldFormat.fileFilter() instanceof FileFilter ff) {
                    return ff;
                }
                return new InternalFileFilter(oldFormat.fileFilter());
            }

            @Override
            public String formatName() {
                return oldFormat.formatName();
            }

            @Override
            public boolean canImport(URL path) {
                return oldFormat.canImport(path);
            }

            @Override
            public boolean canImport(URI path) {
                return oldFormat.canImport(path);
            }
        };
    }

    private static ImportFormatMulti mapMultiFormat(
        de.renew.ioontology.importing.ImportFormatMulti<Drawing> newFormat)
    {
        return new ImportFormatMulti() {
            @Override
            public void addImportFormat(ImportFormat importFormat) {
                newFormat.addImportFormat(mapImportFormat(importFormat));
            }

            @Override
            public void removeImportFormat(ImportFormat importFormat) {
                newFormat.removeImportFormat(mapImportFormat(importFormat));
            }

            @Override
            public ImportFormat[] allImportFormats() {
                return newFormat.getImportFormats().stream()
                    .map(ImportFormatMapper::mapImportFormat).toArray(ImportFormat[]::new);
            }

            @Override
            public List<Drawing> importFiles(URL[] paths) throws Exception {
                return newFormat.importFiles(paths);
            }

            @Override
            public javax.swing.filechooser.FileFilter fileFilter() {
                return mapToSwingFileFilter(newFormat.fileFilter());
            }

            @Override
            public String formatName() {
                return newFormat.formatName();
            }

            @Override
            public boolean canImport(URL path) {
                return newFormat.canImport(path);
            }

            @Override
            public boolean canImport(URI path) {
                return newFormat.canImport(path);
            }
        };
    }

    private static ImportFormat mapSingleFormat(
        de.renew.ioontology.importing.ImportFormat<Drawing> newFormat)
    {
        return new ImportFormat() {
            @Override
            public List<Drawing> importFiles(URL[] paths) throws Exception {
                return newFormat.importFiles(paths);
            }

            @Override
            public javax.swing.filechooser.FileFilter fileFilter() {
                return mapToSwingFileFilter(newFormat.fileFilter());
            }

            @Override
            public String formatName() {
                return newFormat.formatName();
            }

            @Override
            public boolean canImport(URL path) {
                return newFormat.canImport(path);
            }

            @Override
            public boolean canImport(URI path) {
                return newFormat.canImport(path);
            }
        };

    }

    private static javax.swing.filechooser.FileFilter mapToSwingFileFilter(FileFilter fileFilter) {
        return new javax.swing.filechooser.FileFilter() {
            @Override
            public boolean accept(File f) {
                return fileFilter.accept(f);
            }

            @Override
            public String getDescription() {
                return fileFilter.getDescription();
            }
        };
    }

    private static class InternalFileFilter extends javax.swing.filechooser.FileFilter
        implements FileFilter
    {
        private final Predicate<File> _acceptFunction;
        private String _description;

        public InternalFileFilter(javax.swing.filechooser.FileFilter fileFilter) {
            _acceptFunction = fileFilter::accept;
            this._description = fileFilter.getDescription();
        }

        @Override
        public boolean accept(File file) {
            return _acceptFunction.test(file);
        }

        @Override
        public String getDescription() {
            return _description;
        }

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